/* Target-dependent code for Renesas Super-H, for GDB.
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
- Free Software Foundation, Inc.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004 Free Software Foundation, Inc.
This file is part of GDB.
#include "frame-unwind.h"
#include "dwarf2-frame.h"
#include "symtab.h"
-#include "symfile.h"
#include "gdbtypes.h"
#include "gdbcmd.h"
#include "gdbcore.h"
CORE_ADDR saved_sp;
};
-static const char *
-sh_generic_register_name (int reg_nr)
-{
- static char *register_names[] = {
- "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
- "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
- "pc", "pr", "gbr", "vbr", "mach", "macl", "sr",
- "fpul", "fpscr",
- "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7",
- "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15",
- "ssr", "spc",
- "r0b0", "r1b0", "r2b0", "r3b0", "r4b0", "r5b0", "r6b0", "r7b0",
- "r0b1", "r1b1", "r2b1", "r3b1", "r4b1", "r5b1", "r6b1", "r7b1",
- };
- if (reg_nr < 0)
- return NULL;
- if (reg_nr >= (sizeof (register_names) / sizeof (*register_names)))
- return NULL;
- return register_names[reg_nr];
-}
-
static const char *
sh_sh_register_name (int reg_nr)
{
"y0", "y1", "", "", "", "", "", "mod",
"ssr", "spc",
"rs", "re", "", "", "", "", "", "",
- "r0b", "r1b", "r2b", "r3b", "r4b", "r5b", "r6b", "r7b"
- "", "", "", "", "", "", "", "",
+ "r0b", "r1b", "r2b", "r3b", "r4b", "r5b", "r6b", "r7b",
+ "", "", "", "", "", "", "", "",
};
if (reg_nr < 0)
return NULL;
return register_names[reg_nr];
}
+static const char *
+sh_sh4_nofpu_register_name (int reg_nr)
+{
+ static char *register_names[] = {
+ /* general registers 0-15 */
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ /* 16 - 22 */
+ "pc", "pr", "gbr", "vbr", "mach", "macl", "sr",
+ /* 23, 24 */
+ "", "",
+ /* floating point registers 25 - 40 -- not for nofpu target */
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ /* 41, 42 */
+ "ssr", "spc",
+ /* bank 0 43 - 50 */
+ "r0b0", "r1b0", "r2b0", "r3b0", "r4b0", "r5b0", "r6b0", "r7b0",
+ /* bank 1 51 - 58 */
+ "r0b1", "r1b1", "r2b1", "r3b1", "r4b1", "r5b1", "r6b1", "r7b1",
+ /* double precision (pseudo) 59 - 66 -- not for nofpu target */
+ "", "", "", "", "", "", "", "",
+ /* vectors (pseudo) 67 - 70 -- not for nofpu target */
+ "", "", "", "",
+ };
+ if (reg_nr < 0)
+ return NULL;
+ if (reg_nr >= (sizeof (register_names) / sizeof (*register_names)))
+ return NULL;
+ return register_names[reg_nr];
+}
+
+static const char *
+sh_sh4al_dsp_register_name (int reg_nr)
+{
+ static char *register_names[] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "pc", "pr", "gbr", "vbr", "mach", "macl", "sr",
+ "", "dsr",
+ "a0g", "a0", "a1g", "a1", "m0", "m1", "x0", "x1",
+ "y0", "y1", "", "", "", "", "", "mod",
+ "ssr", "spc",
+ "rs", "re", "", "", "", "", "", "",
+ "r0b", "r1b", "r2b", "r3b", "r4b", "r5b", "r6b", "r7b",
+ "", "", "", "", "", "", "", "",
+ };
+ if (reg_nr < 0)
+ return NULL;
+ if (reg_nr >= (sizeof (register_names) / sizeof (*register_names)))
+ return NULL;
+ return register_names[reg_nr];
+}
+
static const unsigned char *
sh_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
{
#define GET_SOURCE_REG(x) (((x) >> 4) & 0xf)
#define GET_TARGET_REG(x) (((x) >> 8) & 0xf)
+/* JSR @Rm 0100mmmm00001011 */
+#define IS_JSR(x) (((x) & 0xf0ff) == 0x400b)
+
/* STS.L PR,@-r15 0100111100100010
r15-4-->r15, PR-->(r15) */
#define IS_STS(x) ((x) == 0x4f22)
}
else if (IS_MOV_SP_FP (inst))
{
- if (!cache->uses_fp)
- cache->uses_fp = 1;
+ cache->uses_fp = 1;
/* At this point, only allow argument register moves to other
registers or argument register moves to @(X,fp) which are
moving the register arguments onto the stack area allocated
}
break;
}
+ else if (IS_JSR (inst))
+ {
+ /* We have found a jsr that has been scheduled into the prologue.
+ If we continue the scan and return a pc someplace after this,
+ then setting a breakpoint on this function will cause it to
+ appear to be called after the function it is calling via the
+ jsr, which will be very confusing. Most likely the next
+ instruction is going to be IS_MOV_SP_FP in the delay slot. If
+ so, note that before returning the current pc. */
+ inst = read_memory_integer (pc + 2, 2);
+ if (IS_MOV_SP_FP (inst))
+ cache->uses_fp = 1;
+ break;
+ }
#if 0 /* This used to just stop when it found an instruction that
was not considered part of the prologue. Now, we just
keep going looking for likely instructions. */
return pc;
}
-/* Should call_function allocate stack space for a struct return?
-
- The ABI says:
+/* The ABI says:
Aggregate types not bigger than 8 bytes that have the same size and
alignment as one of the integer scalar types are returned in the
return FLOAT_ARG0_REGNUM + argreg;
}
+/* Helper function which figures out, if a type is treated like a float type.
+
+ The FPU ABIs have a special way how to treat types as float types.
+ Structures with exactly one member, which is of type float or double, are
+ treated exactly as the base types float or double:
+
+ struct sf {
+ float f;
+ };
+
+ struct sd {
+ double d;
+ };
+
+ are handled the same way as just
+
+ float f;
+
+ double d;
+
+ As a result, arguments of these struct types are pushed into floating point
+ registers exactly as floats or doubles, using the same decision algorithm.
+
+ The same is valid if these types are used as function return types. The
+ above structs are returned in fr0 resp. fr0,fr1 instead of in r0, r0,r1
+ or even using struct convention as it is for other structs. */
+
+static int
+sh_treat_as_flt_p (struct type *type)
+{
+ int len = TYPE_LENGTH (type);
+
+ /* Ordinary float types are obviously treated as float. */
+ if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ return 1;
+ /* Otherwise non-struct types are not treated as float. */
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT)
+ return 0;
+ /* Otherwise structs with more than one memeber are not treated as float. */
+ if (TYPE_NFIELDS (type) != 1)
+ return 0;
+ /* Otherwise if the type of that member is float, the whole type is
+ treated as float. */
+ if (TYPE_CODE (TYPE_FIELD_TYPE (type, 0)) == TYPE_CODE_FLT)
+ return 1;
+ /* Otherwise it's not treated as float. */
+ return 0;
+}
+
static CORE_ADDR
sh_push_dummy_call_fpu (struct gdbarch *gdbarch,
CORE_ADDR func_addr,
CORE_ADDR regval;
char *val;
int len, reg_size = 0;
- int pass_on_stack;
+ int pass_on_stack = 0;
+ int treat_as_flt;
/* first force sp to a 4-byte alignment */
sp = sh_frame_align (gdbarch, sp);
/* Some decisions have to be made how various types are handled.
This also differs in different ABIs. */
pass_on_stack = 0;
- if (len > 16)
- pass_on_stack = 1; /* Types bigger than 16 bytes are passed on stack. */
/* Find out the next register to use for a floating point value. */
- if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ treat_as_flt = sh_treat_as_flt_p (type);
+ if (treat_as_flt)
flt_argreg = sh_next_flt_argreg (len);
+ /* In contrast to non-FPU CPUs, arguments are never split between
+ registers and stack. If an argument doesn't fit in the remaining
+ registers it's always pushed entirely on the stack. */
+ else if (len > ((ARGLAST_REGNUM - argreg + 1) * 4))
+ pass_on_stack = 1;
while (len > 0)
{
- if ((TYPE_CODE (type) == TYPE_CODE_FLT
- && flt_argreg > FLOAT_ARGLAST_REGNUM)
- || argreg > ARGLAST_REGNUM || pass_on_stack)
+ if ((treat_as_flt && flt_argreg > FLOAT_ARGLAST_REGNUM)
+ || (!treat_as_flt && (argreg > ARGLAST_REGNUM
+ || pass_on_stack)))
{
- /* The remainder of the data goes entirely on the stack,
- 4-byte aligned. */
+ /* The data goes entirely on the stack, 4-byte aligned. */
reg_size = (len + 3) & ~3;
write_memory (sp + stack_offset, val, reg_size);
stack_offset += reg_size;
}
- else if (TYPE_CODE (type) == TYPE_CODE_FLT
- && flt_argreg <= FLOAT_ARGLAST_REGNUM)
+ else if (treat_as_flt && flt_argreg <= FLOAT_ARGLAST_REGNUM)
{
/* Argument goes in a float argument register. */
reg_size = register_size (gdbarch, flt_argreg);
regval = extract_unsigned_integer (val, reg_size);
+ /* In little endian mode, float types taking two registers
+ (doubles on sh4, long doubles on sh2e, sh3e and sh4) must
+ be stored swapped in the argument registers. The below
+ code first writes the first 32 bits in the next but one
+ register, increments the val and len values accordingly
+ and then proceeds as normal by writing the second 32 bits
+ into the next register. */
+ if (TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE
+ && TYPE_LENGTH (type) == 2 * reg_size)
+ {
+ regcache_cooked_write_unsigned (regcache, flt_argreg + 1,
+ regval);
+ val += reg_size;
+ len -= reg_size;
+ regval = extract_unsigned_integer (val, reg_size);
+ }
regcache_cooked_write_unsigned (regcache, flt_argreg++, regval);
}
- else if (argreg <= ARGLAST_REGNUM)
+ else if (!treat_as_flt && argreg <= ARGLAST_REGNUM)
{
/* there's room in a register */
reg_size = register_size (gdbarch, argreg);
regval = extract_unsigned_integer (val, reg_size);
regcache_cooked_write_unsigned (regcache, argreg++, regval);
}
- /* Store the value reg_size bytes at a time. This means that things
- larger than reg_size bytes may go partly in registers and partly
- on the stack. */
+ /* Store the value one register at a time or in one step on stack. */
len -= reg_size;
val += reg_size;
}
sh3e_sh4_extract_return_value (struct type *type, struct regcache *regcache,
void *valbuf)
{
- if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ if (sh_treat_as_flt_p (type))
{
int len = TYPE_LENGTH (type);
int i, regnum = FP0_REGNUM;
for (i = 0; i < len; i += 4)
- regcache_raw_read (regcache, regnum++, (char *) valbuf + i);
+ if (TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE)
+ regcache_raw_read (regcache, regnum++, (char *) valbuf + len - 4 - i);
+ else
+ regcache_raw_read (regcache, regnum++, (char *) valbuf + i);
}
else
sh_default_extract_return_value (type, regcache, valbuf);
sh3e_sh4_store_return_value (struct type *type, struct regcache *regcache,
const void *valbuf)
{
- if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ if (sh_treat_as_flt_p (type))
{
int len = TYPE_LENGTH (type);
int i, regnum = FP0_REGNUM;
(long) read_register (FP0_REGNUM + 15));
}
+static void
+sh4_nofpu_show_regs (void)
+{
+ printf_filtered ("PC=%s SR=%08lx PR=%08lx MACH=%08lx MACHL=%08lx\n",
+ paddr (read_register (PC_REGNUM)),
+ (long) read_register (SR_REGNUM),
+ (long) read_register (PR_REGNUM),
+ (long) read_register (MACH_REGNUM),
+ (long) read_register (MACL_REGNUM));
+
+ printf_filtered ("GBR=%08lx VBR=%08lx",
+ (long) read_register (GBR_REGNUM),
+ (long) read_register (VBR_REGNUM));
+ printf_filtered (" SSR=%08lx SPC=%08lx",
+ (long) read_register (SSR_REGNUM),
+ (long) read_register (SPC_REGNUM));
+
+ printf_filtered
+ ("\nR0-R7 %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+ (long) read_register (0), (long) read_register (1),
+ (long) read_register (2), (long) read_register (3),
+ (long) read_register (4), (long) read_register (5),
+ (long) read_register (6), (long) read_register (7));
+ printf_filtered ("R8-R15 %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+ (long) read_register (8), (long) read_register (9),
+ (long) read_register (10), (long) read_register (11),
+ (long) read_register (12), (long) read_register (13),
+ (long) read_register (14), (long) read_register (15));
+}
+
static void
sh_dsp_show_regs (void)
{
because they are stored as 4 individual FP elements. */
static void
-sh_sh4_register_convert_to_virtual (int regnum, struct type *type,
- char *from, char *to)
+sh_register_convert_to_virtual (int regnum, struct type *type,
+ char *from, char *to)
{
if (regnum >= DR0_REGNUM && regnum <= DR_LAST_REGNUM)
{
}
static void
-sh_sh4_register_convert_to_raw (struct type *type, int regnum,
- const void *from, void *to)
+sh_register_convert_to_raw (struct type *type, int regnum,
+ const void *from, void *to)
{
if (regnum >= DR0_REGNUM && regnum <= DR_LAST_REGNUM)
{
+ register_size (gdbarch,
base_regnum) * portion));
/* We must pay attention to the endiannes. */
- sh_sh4_register_convert_to_virtual (reg_nr,
- gdbarch_register_type (gdbarch,
- reg_nr),
- temp_buffer, buffer);
+ sh_register_convert_to_virtual (reg_nr,
+ gdbarch_register_type (gdbarch, reg_nr),
+ temp_buffer, buffer);
}
else if (reg_nr >= FV0_REGNUM && reg_nr <= FV_LAST_REGNUM)
{
base_regnum = dr_reg_base_num (reg_nr);
/* We must pay attention to the endiannes. */
- sh_sh4_register_convert_to_raw (gdbarch_register_type (gdbarch, reg_nr),
- reg_nr, buffer, temp_buffer);
+ sh_register_convert_to_raw (gdbarch_register_type (gdbarch, reg_nr),
+ reg_nr, buffer, temp_buffer);
/* Write the real regs for which this one is an alias. */
for (portion = 0; portion < 2; portion++)
return SIM_SH_RS_REGNUM;
if (nr == RE_REGNUM)
return SIM_SH_RE_REGNUM;
- if (nr >= R0_BANK_REGNUM && nr <= R7_BANK_REGNUM)
- return nr - R0_BANK_REGNUM + SIM_SH_R0_BANK_REGNUM;
+ if (nr >= DSP_R0_BANK_REGNUM && nr <= DSP_R7_BANK_REGNUM)
+ return nr - DSP_R0_BANK_REGNUM + SIM_SH_R0_BANK_REGNUM;
return nr;
}
break;
case bfd_mach_sh3_dsp:
+ case bfd_mach_sh4al_dsp:
sh_show_regs = sh3_dsp_show_regs;
break;
case bfd_mach_sh4:
+ case bfd_mach_sh4a:
sh_show_regs = sh4_show_regs;
break;
+ case bfd_mach_sh4_nofpu:
+ case bfd_mach_sh4a_nofpu:
+ sh_show_regs = sh4_nofpu_show_regs;
+ break;
+
case bfd_mach_sh5:
sh_show_regs = sh64_show_regs;
/* SH5 is handled entirely in sh64-tdep.c */
set_gdbarch_store_return_value (gdbarch, sh_default_store_return_value);
set_gdbarch_extract_return_value (gdbarch, sh_default_extract_return_value);
- set_gdbarch_extract_struct_value_address (gdbarch,
- sh_extract_struct_value_address);
+ set_gdbarch_deprecated_extract_struct_value_address (gdbarch, sh_extract_struct_value_address);
set_gdbarch_skip_prologue (gdbarch, sh_skip_prologue);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
- set_gdbarch_decr_pc_after_break (gdbarch, 0);
- set_gdbarch_function_start_offset (gdbarch, 0);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_nofpu);
- set_gdbarch_frame_args_skip (gdbarch, 0);
- set_gdbarch_frameless_function_invocation (gdbarch,
- frameless_look_for_prologue);
+ set_gdbarch_deprecated_frameless_function_invocation (gdbarch, legacy_frameless_look_for_prologue);
set_gdbarch_believe_pcc_promotion (gdbarch, 1);
set_gdbarch_frame_align (gdbarch, sh_frame_align);
break;
case bfd_mach_sh4:
+ case bfd_mach_sh4a:
set_gdbarch_register_name (gdbarch, sh_sh4_register_name);
set_gdbarch_register_type (gdbarch, sh_sh4_register_type);
set_gdbarch_fp0_regnum (gdbarch, 25);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
break;
+ case bfd_mach_sh4_nofpu:
+ case bfd_mach_sh4a_nofpu:
+ set_gdbarch_register_name (gdbarch, sh_sh4_nofpu_register_name);
+ break;
+
+ case bfd_mach_sh4al_dsp:
+ set_gdbarch_register_name (gdbarch, sh_sh4al_dsp_register_name);
+ set_gdbarch_register_sim_regno (gdbarch, sh_dsp_register_sim_regno);
+ break;
+
default:
- set_gdbarch_register_name (gdbarch, sh_generic_register_name);
+ set_gdbarch_register_name (gdbarch, sh_sh_register_name);
break;
}