diff options
Diffstat (limited to 'gdb/sparc-pinsn.c')
-rw-r--r-- | gdb/sparc-pinsn.c | 811 |
1 files changed, 811 insertions, 0 deletions
diff --git a/gdb/sparc-pinsn.c b/gdb/sparc-pinsn.c new file mode 100644 index 0000000..e7aba32 --- /dev/null +++ b/gdb/sparc-pinsn.c @@ -0,0 +1,811 @@ +/* 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 <stdio.h> + +#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); +} |