aboutsummaryrefslogtreecommitdiff
path: root/gdb/sparc-pinsn.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/sparc-pinsn.c')
-rw-r--r--gdb/sparc-pinsn.c811
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);
+}