binutils-gdb/gdb/sp64-tdep.c

289 lines
7.5 KiB
C
Raw Normal View History

/* Target-dependent code for the SPARC 64 for GDB, the GNU debugger.
Copyright 1986, 1987, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
Contributed by Doug Evans (dje@cygnus.com).
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "defs.h"
#include "frame.h"
#include "inferior.h"
#include "obstack.h"
#include "target.h"
/*#include "symfile.h" /* for objfiles.h */
/*#include "objfiles.h" /* for find_pc_section */
/* This file contains replacements and additions to sparc-tdep.c only.
Some of this code has been written for a day when we can merge at least
some of this with sparc-tdep.c. Macro TARGET_SPARC64 exists to allow some
code to potentially be used by both. */
#define TARGET_SPARC64 1 /* later make a config parm or some such */
/* From infrun.c */
extern int stop_after_trap;
/* Branches with prediction are treated like their non-predicting cousins. */
/* FIXME: What about floating point branches? */
typedef enum
{
Error, not_branch, bicc, bicca, ba, baa, ticc, ta, done_retry
} branch_type;
/* Simulate single-step ptrace call for sun4. Code written by Gary
Beihl (beihl@mcc.com). */
/* npc4 and next_pc describe the situation at the time that the
step-breakpoint was set, not necessary the current value of NPC_REGNUM. */
static CORE_ADDR next_pc, npc4, target;
static int brknpc4, brktrg;
typedef char binsn_quantum[BREAKPOINT_MAX];
static binsn_quantum break_mem[3];
/* Non-zero if we just simulated a single-step ptrace call. This is
needed because we cannot remove the breakpoints in the inferior
process until after the `wait' in `wait_for_inferior'. Used for
sun4. */
int one_stepped;
/* sparc64_single_step() is called just before we want to resume the inferior,
if we want to single-step it but there is no hardware or kernel single-step
support (as on all SPARCs). We find all the possible targets of the
coming instruction and breakpoint them.
single_step is also called just after the inferior stops. If we had
set up a simulated single-step, we undo our damage. */
/* FIXME: When the code is releasable, sparc's single step could become this
one, removing the duplication. */
void
sparc64_single_step (ignore)
int ignore; /* pid, but we don't need it */
{
branch_type br, isbranch();
CORE_ADDR pc;
long pc_instruction;
if (!one_stepped)
{
/* Always set breakpoint for NPC. */
next_pc = read_register (NPC_REGNUM);
npc4 = next_pc + 4; /* branch not taken */
target_insert_breakpoint (next_pc, break_mem[0]);
/* printf_unfiltered ("set break at %x\n",next_pc); */
pc = read_register (PC_REGNUM);
pc_instruction = read_memory_integer (pc, sizeof(pc_instruction));
br = isbranch (pc_instruction, pc, &target);
brknpc4 = brktrg = 0;
if (br == bicca)
{
/* Conditional annulled branch will either end up at
npc (if taken) or at npc+4 (if not taken).
Trap npc+4. */
brknpc4 = 1;
target_insert_breakpoint (npc4, break_mem[1]);
}
else if ((br == baa && target != next_pc)
|| (TARGET_SPARC64 && br == done_retry))
{
/* Unconditional annulled branch will always end up at
the target. */
brktrg = 1;
target_insert_breakpoint (target, break_mem[2]);
}
/* We are ready to let it go */
one_stepped = 1;
return;
}
else
{
/* Remove breakpoints */
target_remove_breakpoint (next_pc, break_mem[0]);
if (brknpc4)
target_remove_breakpoint (npc4, break_mem[1]);
if (brktrg)
target_remove_breakpoint (target, break_mem[2]);
one_stepped = 0;
}
}
CORE_ADDR
sparc64_extract_struct_value_address (regbuf)
char regbuf[REGISTER_BYTES];
{
CORE_ADDR addr;
/* FIXME: We assume a non-leaf function. */
addr = read_register (I0_REGNUM);
return addr;
}
/* Check instruction at ADDR to see if it is an annulled branch or other
instruction whose npc isn't pc+4 (eg: trap, done, retry).
All other instructions will go to NPC or will trap.
Set *TARGET if we find a candidate branch; set to zero if not. */
branch_type
isbranch (instruction, addr, target)
long instruction;
CORE_ADDR addr, *target;
{
branch_type val = not_branch;
long int offset; /* Must be signed for sign-extend. */
union
{
unsigned long int code;
struct
{
unsigned int op:2;
unsigned int a:1;
unsigned int cond:4;
unsigned int op2:3;
unsigned int disp22:22;
} b;
struct
{
unsigned int op:2;
unsigned int a:1;
unsigned int cond:4;
unsigned int op2:3;
unsigned int cc:2;
unsigned int p:1;
unsigned int disp19:19;
} bp;
struct
{
unsigned int op:2;
unsigned int a:1;
unsigned int zero:1;
unsigned int rcond:3;
unsigned int op2:3;
unsigned int disp16hi:2;
unsigned int p:1;
unsigned int rs1:5;
unsigned int disp16lo:14;
} bpr;
struct
{
unsigned int op:2;
unsigned int fcn:5;
unsigned int op3:6;
unsigned int reserved:19;
} dr;
} insn;
*target = 0;
insn.code = instruction;
if (insn.b.op == 0
&& (insn.b.op2 == 1 || insn.b.op2 == 2 || insn.b.op2 ==3
|| insn.b.op2 == 5 || insn.b.op2 == 6))
{
if (insn.b.cond == 8)
val = insn.b.a ? baa : ba;
else
val = insn.b.a ? bicca : bicc;
switch (insn.b.op2)
{
case 1: /* bpcc */
offset = 4 * ((int) (insn.bp.disp19 << 13) >> 13);
break;
case 2: /* bicc */
offset = 4 * ((int) (insn.b.disp22 << 10) >> 10);
break;
case 3: /* bpr */
offset = 4 * ((int) ((insn.bpr.disp16hi << 10)
|| (insn.bpr.disp16lo << 18)) >> 13);
break;
case 5: /* fbpfcc */
offset = 4 * ((int) (insn.bp.disp19 << 13) >> 13);
break;
case 6: /* fbfcc */
offset = 4 * ((int) (insn.b.disp22 << 10) >> 10);
break;
}
*target = addr + offset;
}
else if (insn.dr.op == 2 && insn.dr.op3 == 62)
{
if (insn.dr.fcn == 0)
{
/* done */
*target = read_register (TNPC_REGNUM);
val = done_retry;
}
else if (insn.dr.fcn == 1)
{
/* retry */
*target = read_register (TPC_REGNUM);
val = done_retry;
}
}
return val;
}
/* PRINT_REGISTER_HOOK routine.
Pretty print various registers. */
static void
dump_ccreg (reg, val)
char *reg;
int val;
{
printf_unfiltered ("%s:%s,%s,%s,%s", reg,
val & 8 ? "N" : "NN",
val & 4 ? "Z" : "NZ",
val & 2 ? "O" : "NO",
val & 1 ? "C" : "NC"
);
}
void
sparc_print_register_hook (regno)
int regno;
{
if (((unsigned) (regno) - FP0_REGNUM < FP_MAX_REGNUM - FP0_REGNUM)
&& ((regno) & 1) == 0)
{
char doublereg[8]; /* two float regs */
if (!read_relative_register_raw_bytes ((regno), doublereg))
{
printf_unfiltered("\t");
print_floating (doublereg, builtin_type_double, gdb_stdout);
}
}
else if ((regno) == CCR_REGNUM)
{
int ccr = read_register (CCR_REGNUM);
printf_unfiltered("\t");
dump_ccreg ("xcc", ccr >> 4);
printf_unfiltered(", ");
dump_ccreg ("icc", ccr & 15);
}
}