/* Print sparc instructions for GDB, the GNU debugger. Copyright (C) 1986, 1987 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@mcc.com) GDB is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. Refer to the GDB General Public License for full details. Everyone is granted permission to copy, modify and redistribute GDB, but only under the conditions described in the GDB General Public License. A copy of this license is supposed to have been given to you along with GDB so you can know your rights and responsibilities. It should be in a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. In other words, go ahead and share GDB, but don't try to stop anyone else from sharing it farther. Help stamp out software hoarding! */ #include #include "defs.h" #include "param.h" #include "symtab.h" #include "sparc-opcode.h" /* sparc instructions are never longer than this many bytes. */ #define MAXLEN 4 /* Print the sparc instruction at address MEMADDR in debugged memory, on STREAM. Returns length of the instruction, in bytes, which is always 4. */ struct op1_fmt { unsigned op1 : 2; unsigned dummy : 30; }; struct op2_fmt { unsigned dummy1 : 7; unsigned op2 : 3; unsigned dummy2 : 22; }; struct op3_fmt { unsigned dummy1 : 7; unsigned op3 : 6; unsigned dummy2 : 19; }; struct call_fmt { unsigned op : 2; unsigned disp : 30; }; struct sethi_fmt { unsigned op : 2; unsigned rd : 5; unsigned op2 : 3; unsigned imm : 22; }; struct branch_fmt { unsigned op : 2; unsigned a : 1; unsigned cond : 4; unsigned op2 : 3; unsigned disp : 22; /* this should really be signed. */ }; struct ldst_fmt { unsigned op : 2; unsigned rd : 5; unsigned op3 : 6; unsigned rs1 : 5; unsigned i : 1; unsigned asi : 8; unsigned rs2 : 5; }; struct arith_imm_fmt { unsigned op : 2; unsigned rd : 5; unsigned op3 : 6; unsigned rs1 : 5; unsigned i : 1; unsigned simm : 13; }; struct arith_fmt { unsigned op : 2; unsigned rd : 5; unsigned op3 : 6; unsigned rs1 : 5; unsigned i : 1; unsigned opf : 8; unsigned rs2 : 5; }; union insn_fmt { struct op1_fmt op1; struct op2_fmt op2; struct op3_fmt op3; struct call_fmt call; struct sethi_fmt sethi; struct branch_fmt branch; struct ldst_fmt ldst; struct arith_imm_fmt arith_imm; struct arith_fmt arith; int intval; float floatval; /* ?? */ }; typedef enum { Error, not_branch, bicc, bicca, ba, baa, ticc, ta, } branch_type; static char *icc_name[] = { "~", "eq", "le", "lt", "leu", "ltu", "neg", "vs", "", "ne", "gt", "ge", "gtu", "geu", "pos", "vc"}; static char *fcc_name[] = { "~fb", "fbne", "fblg", "fbul", "fbl", "fbug", "fbg", "fbu", "fb", "fbe", "fbue", "fbge", "fbuge", "fble", "fbule", "fbo"}; static char *ccc_name[] = { "~cb", "cb123", "cb12", "cb13", "cb1", "cb23", "cb2", "cb3", "cb", "cb0", "cb03", "cb02", "cb023", "cb01", "cb013", "cb012"}; static char *arith_name[] = { "add", "and", "or", "xor", "sub", "andn", "orn", "xnor", "addx", 0, 0, 0, "subx", 0, 0, 0}; static char *xarith_name[] = { "taddcc", "tsubcc", "taddcctv", "tsubcctv", "mulscc", "sll", "srl", "sra"}; static char *state_reg_name[] = { "%y", "%psr", "%wim", "%tbr", 0, 0, 0, 0}; static char *ldst_i_name[] = { "ld", "ldub", "lduh", "ldd", "st", "stb", "sth", "std", 0, "ldsb", "ldsh", 0, 0, "ldstub", 0, "swap", "lda", "lduba", "lduha", "ldda", "sta", "stba", "stha", "stda", 0, "ldsba", "ldsha", 0, 0, "ldstuba", 0, "swapa"}; static char *ldst_f_name[] = { "ldf", "ldfsr", 0, "lddf", "stf", "stfsr", "stdfq", "stdf"}; static char *ldst_c_name[] = { "ldc", "ldcsr", 0, "lddc", "stc", "stcsr", "stdcq", "stdc"}; static int this_sethi_target = -1; static int last_sethi_target = -1; static int sethi_value = 0; static void fprint_addr1 (); static void fprint_ldst (); static void fprint_f_ldst (); static void fprint_c_ldst (); static void fprint_fpop (); int print_insn (memaddr, stream) CORE_ADDR memaddr; FILE *stream; { union insn_fmt insn; int disp22; read_memory (memaddr, &insn, MAXLEN); this_sethi_target = -1; switch (insn.op1.op1) { case 1: /* CALL format. */ fprintf (stream, "call "); print_address (memaddr + (insn.call.disp << 2), stream); break; case 0: /* Bicc, FBfcc, CBccc, SETHI format. */ switch (insn.op2.op2) { case 0: fprintf (stream, "unimp"); break; case 2: /* Bicc. */ fprintf (stream, "b%s", icc_name[insn.branch.cond]); if (insn.branch.a) fprintf (stream, ",a "); else fprintf (stream, " "); disp22 = insn.branch.disp; disp22 = ((disp22 << 10) >> 10); print_address (memaddr + (disp22 << 2), stream); break; case 4: /* SETHI. */ fprintf (stream, "sethi %%hi(0x%x),%s", insn.sethi.imm << 10, reg_names[insn.sethi.rd]); this_sethi_target = insn.sethi.rd; sethi_value = insn.sethi.imm << 12; break; case 6: /* FBdfcc. */ fprintf (stream, "fb%s", fcc_name[insn.branch.cond]); if (insn.branch.a) fprintf (stream, ",a "); else fprintf (stream, " "); disp22 = insn.branch.disp; disp22 = ((disp22 << 10) >> 10); print_address (memaddr + (disp22 << 2), stream); break; case 7: /* CBccc. */ fprintf (stream, "cb%s", ccc_name[insn.branch.cond]); if (insn.branch.a) fprintf (stream, ",a "); else fprintf (stream, " "); disp22 = insn.branch.disp; disp22 = ((disp22 << 10) >> 10); print_address (memaddr + (disp22 << 2), stream); break; default: fprintf (stream, "0x%x (illegal op2 format)", insn.intval); break; } break; case 2: { /* vaguely arithmetic insns. */ char *rd = reg_names[insn.arith.rd]; char *rs1 = reg_names[insn.arith.rs1]; if (insn.op3.op3 <= 28) { /* Arithmetic insns, with a few unimplemented. */ register int affect_cc = insn.op3.op3 & 16; char *name = arith_name[insn.op3.op3 ^ affect_cc]; char *tmp = affect_cc ? "cc" : ""; if (name == 0) { fprintf (stream, "0x%08x (unimplemented arithmetic insn)", insn.intval); } else if (insn.arith.i) { fprintf (stream, "%s%s %s,0x%x,%s", name, tmp, rs1, insn.arith_imm.simm, rd); if (last_sethi_target == insn.arith.rd) { fprintf (stream, "\t! "); print_address (sethi_value + insn.arith_imm.simm); } } else { fprintf (stream, "%s%s %s,%s,%s", name, tmp, rs1, reg_names[insn.arith.rs2], rd); } break; } if (insn.op3.op3 < 32) { fprintf (stream, "0x%08x (unimplemented arithmetic insn)", insn.intval); break; } else { int op = insn.op3.op3 ^ 32; if (op < 8) { char *name = xarith_name[op]; /* tagged add/sub insns and shift insns. */ if (insn.arith.i) { int i = insn.arith_imm.simm; if (op > 4) /* Its a shift insn. */ i &= 31; fprintf (stream, "%s %s,0x%x,%s", name, rs1, i, rd); } else { fprintf (stream, "%s %s,%s,%s", name, rs1, reg_names[insn.arith.rs2], rd); } break; } if (op < 20) { /* read/write state registers. */ char *sr = state_reg_name[op & 7]; if (sr == 0) fprintf (stream, "0x%08x (unimplemented state register insn", insn.intval); else fprintf (stream, "%s %s,%s", op & 16 ? "wr" : "rd", sr, rd); break; } if (op < 22) { /* floating point insns. */ int opcode = insn.arith.opf; fprint_fpop (stream, insn, op & 3, opcode); break; } if (op < 24) { /* coprocessor insns. */ char *rs2 = reg_names[insn.arith.rs2]; int opcode = insn.arith.opf; fprintf (stream, "cpop%d rs1=%s,rs2=%s,op=0x%x,rd=%s", op & 1, rs1, rs2, opcode, rd); break; } switch (op) { char *rndop_ptr; case 24: fprint_addr1 (stream, "jumpl", insn); break; case 25: fprint_addr1 (stream, "rett", insn); break; case 26: { char rndop_buf[32]; sprintf (rndop_buf, "t%s", icc_name[insn.branch.cond]); fprint_addr1 (stream, rndop_buf, insn); } break; case 27: fprint_addr1 (stream, "iflush", insn); break; case 28: rndop_ptr = "save"; case 29: if (op == 29) rndop_ptr = "restore"; if (insn.arith.i) { fprintf (stream, "%s %s,0x%x,%s", rndop_ptr, rs1, ((insn.arith_imm.simm << 19) >> 19), rd); } else { fprintf (stream, "%s %s,%s,%s", rndop_ptr, rs1, reg_names[insn.arith.rs2], rd); } break; case 30: case 31: fprintf (stream, "0x%08x (unimplemented op3 insn)", insn.intval); break; } break; } } case 3: /* load and store insns. */ { char *rd = reg_names[insn.arith.rd]; char *rs1 = reg_names[insn.arith.rs1]; int op = insn.arith.op3; if ((op & 32) == 0) { /* Integer ops. */ fprint_ldst (stream, insn, op); break; } if ((op & 16) == 0) { /* Float ops. */ op ^= 32; if (op <= 7) { fprint_f_ldst (stream, insn, op); } else fprintf (stream, "0x%08x (unimplemented float load/store insn)", insn.intval); } else { /* Coprocessor ops. */ op ^= (32+16); if (op <= 7) { fprint_c_ldst (stream, insn, op); } else fprintf (stream, "0x%08x (unimplemented coprocessor load/store insn)", insn.intval); } break; } } return 4; } /* It would be nice if this routine could print out a symbolic address when appropriate. */ static void fprint_addr1 (stream, name, insn) FILE *stream; char *name; union insn_fmt insn; { char *rs1 = reg_names[insn.arith.rs1]; char *rd = reg_names[insn.arith.rd]; if (insn.arith.i) { fprintf (stream, "%s %s,0x%x,%s", name, rs1, insn.arith_imm.simm, rd); } else { fprintf (stream, "%s %s,%s,%s", name, rs1, reg_names[insn.arith.rs2], rd); } } static void fprint_mem (stream, insn) FILE *stream; union insn_fmt insn; { char *reg_name = reg_names[insn.arith.rs1]; if (insn.arith.i) { if (insn.arith_imm.simm == 0) fprintf (stream, "[%s]", reg_name); else if (insn.arith_imm.simm & 0x1000) fprintf (stream, "[%s-0x%x]", reg_name, - (insn.arith_imm.simm | 0xffffe000)); else fprintf (stream, "[%s+0x%x]", reg_name, insn.arith_imm.simm); } else { if (insn.arith.rs2 == 0) fprintf (stream, "[%s]", reg_name); else fprintf (stream, "[%s,%s]", reg_names[insn.arith.rs2], reg_name); } } static void fprint_ldst (stream, insn, op) FILE *stream; union insn_fmt insn; int op; { char *name = ldst_i_name[op]; char *rd = reg_names[insn.arith.rd]; if (name) { if (name[0] == 's') { fprintf (stream, "%s %s,", name, rd); fprint_mem (stream, insn); } else { fprintf (stream, "%s ", name); fprint_mem (stream, insn); fprintf (stream, ",%s", rd); } } else fprintf (stream, "0x%08x (unimplemented load/store insn)", insn.intval); } static void fprint_f_ldst (stream, insn, op) FILE *stream; union insn_fmt insn; int op; { char *name = ldst_f_name[op]; if (name) { char *rd = reg_names[insn.arith.rd + 32]; if (name[0] == 's') { fprintf (stream, "%s %s,", name, rd); fprint_mem (stream, insn); } else { fprintf (stream, "%s ", name); fprint_mem (stream, insn); fprintf (stream, ",%s", rd); } } else fprintf (stream, "0x%08x (unimplemented float load/store insn)", insn.intval); } static void fprint_c_ldst (stream, insn, op) FILE *stream; union insn_fmt insn; int op; { char *name = ldst_c_name[op]; if (name) { if (name[0] == 's') { fprintf (stream, "%s %%cpreg(%d),", name, insn.arith.rs1); fprint_mem (stream, insn); } else { fprintf (stream, "%s "); fprint_mem (stream, insn); fprintf (stream, ",%%cpreg(%d)", insn.arith.rd); } } else fprintf (stream, "0x%08x (unimplemented coprocessor load/store insn)", insn.intval); } static void fprint_fpop (stream, insn, op, opcode) FILE *stream; union insn_fmt insn; int op, opcode; { char *name; char *rs1, *rs2, *rd; switch (op) { case 0: rs2 = reg_names[insn.arith.rs2 + 32]; rd = reg_names[insn.arith.rd + 32]; if ((opcode ^ 0x2f) <= 0x2f) { switch (opcode) { case 0x1: name = "fmovs"; break; case 0x5: name = "fnegs"; break; case 0x9: name = "fabss"; break; case 0x29: name = "fsqrts"; break; case 0x2a: name = "fsqrtd"; break; case 0x2b: name = "fsqrtx"; break; } fprintf (stream, "%s %s,%s", name, rs2, rd); return; } if ((opcode ^ 0x5f) <= 0x5f) { rs1 = reg_names[insn.arith.rs1 + 32]; switch (opcode) { case 0x41: name = "fadds"; break; case 0x42: name = "faddd"; break; case 0x43: name = "faddx"; break; case 0x45: name = "fsubs"; break; case 0x46: name = "fsubd"; break; case 0x47: name = "fsubx"; break; case 0x49: name = "fmuls"; break; case 0x4a: name = "fmuld"; break; case 0x4b: name = "fmulx"; break; case 0x4d: name = "fdivs"; break; case 0x4e: name = "fdivd"; break; case 0x4f: name = "fdivx"; break; default: goto unimplemented; } if ((opcode & 0x10) == 0) fprintf (stream, "%s %s,%s,%s", name, rs1, rs2, rd); else fprintf (stream, "%s %s,%s", name, rs1, rs2); return; } if ((opcode ^ 0xdf) <= 0xdf) { switch (opcode) { case 0xc4: name = "fitos"; break; case 0xc8: name = "fitod"; break; case 0xcc: name = "fitox"; break; case 0xd1: name = "fstoi"; break; case 0xd2: name = "fdtoi"; break; case 0xd3: name = "fxtoi"; break; case 0xc9: name = "fstod"; break; case 0xcd: name = "fstox"; break; case 0xc6: name = "fdtos"; break; case 0xce: name = "fdtox"; break; case 0xc7: name = "fxtos"; break; case 0xcb: name = "fxtod"; break; default: goto unimplemented; } fprintf (stream, "%s %s,%s", name, rs2, rd); return; } goto unimplemented; case 1: rs1 = reg_names[insn.arith.rs1 + 32]; rs2 = reg_names[insn.arith.rs2 + 32]; if ((opcode ^ 0x57) <= 0x57) { switch (opcode) { case 0x51: name = "fcmps"; break; case 0x52: name = "fcmpd"; break; case 0x53: name = "fcmpx"; break; case 0x55: name = "fcmpes"; break; case 0x56: name = "fcmped"; break; case 0x57: name = "fcmpex"; break; default: goto unimplemented; } fprintf (stream, "%s %s,%s", name, rs1, rs2); return; } else goto unimplemented; case 2: case 3: goto unimplemented; } unimplemented: fprintf (stream, "0x%08x (unimplemented fpop insn)", insn.intval); } /* Set *target if we find a branch */ branch_type isabranch (addr, target) CORE_ADDR addr, *target; { union insn_fmt instr; branch_type val = not_branch; long offset; /* Must be signed for sign-extend */ *target = 0; instr.intval = read_memory_integer (addr, 4); /* printf("intval = %x\n",instr.intval); */ switch (instr.op1.op1) { case 0: /* Format 2 */ switch(instr.op2.op2) { case 2: case 6: /* BICC & FBCC */ if (instr.branch.cond == 8) val = instr.branch.a ? baa : ba; else val = instr.branch.a ? bicca : bicc; /* 22 bits, sign extended */ offset = ((instr.branch.disp << 10) >> 10); *target = addr + offset; break; } break; } /*printf("isabranch ret: %d\n",val); */ return val; } CORE_ADDR skip_prologue (pc) CORE_ADDR pc; { union { struct insn_fmt insn; int i; } x; int dest = -1; x.i = read_memory_integer (pc, 4); if (x.insn.sethi.op == 0 && x.insn.sethi.op2 == 4) { dest = x.insn.sethi.rd; pc += 4; x.i = read_memory_integer (pc, 4); } if (x.insn.arith_imm.op == 2 && x.insn.arith_imm.i == 1 && (x.insn.arith_imm.rd == 1 || x.insn.arith_imm.rd == dest)) { pc += 4; x.i = read_memory_integer (pc, 4); } if (x.insn.arith.op == 2 && (x.insn.arith.op3 ^ 32) == 28) { pc += 4; } return pc; } CORE_ADDR frame_saved_pc (frame, next_frame) CORE_ADDR frame; CORE_ADDR next_frame; { CORE_ADDR prev_pc; if (next_frame) prev_pc = GET_RWINDOW_REG (next_frame, rw_in[7]); else if (frame) prev_pc = GET_RWINDOW_REG (read_register (SP_REGNUM), rw_in[7]); else error ("frame_saved_pc called without a frame"); return PC_ADJUST (prev_pc); }