Tue Nov 12 12:16:40 1996 Michael Snyder <msnyder@cleaver.cygnus.com>
authorMichael Snyder <msnyder@vmware.com>
Tue, 12 Nov 1996 20:19:17 +0000 (20:19 +0000)
committerMichael Snyder <msnyder@vmware.com>
Tue, 12 Nov 1996 20:19:17 +0000 (20:19 +0000)
        * sh-tdep.c: Add functionality for target function calls.
        * config/sh/tm-sh.h: Add support for target function calls.
This is a safety check-in: everything works, and there'll be another
clean-up round shortly.

gdb/ChangeLog
gdb/config/sh/tm-sh.h
gdb/sh-tdep.c

index c23085f56c1c097df5e6c7135ea359d3443230fe..c266de3643789803ab55d79cd42d2083bebdeafa 100644 (file)
@@ -1,9 +1,14 @@
+Tue Nov 12 12:16:40 1996  Michael Snyder  <msnyder@cleaver.cygnus.com>
+
+       * sh-tdep.c: Add functionality for target function calls.
+       * config/sh/tm-sh.h: Add support for target function calls.
+
 start-sanitize-m32r
 Tue Nov 12 12:06:58 1996  Michael Snyder  <msnyder@cleaver.cygnus.com>
         
-               * m32r-tdep.c: Add functionality for target function calls.
-               * valops.c:    Small change to support target function calls.
-               * config/m32r/tm-m32r.h: Add support for target function calls.
+       * m32r-tdep.c: Add functionality for target function calls.
+       * valops.c:    Small change to support target function calls.
+       * config/m32r/tm-m32r.h: Add support for target function calls.
 end-sanitize-m32r
         
 Mon Nov 11 17:15:59 1996  Geoffrey Noer  <noer@cygnus.com>
index 402cbce08d401195faa9cd42d4fa544bcf3c22b6..2b879d1638c0b411afcad150f1976a0119b9c109 100644 (file)
@@ -19,6 +19,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 /* Contributed by Steve Chamberlain sac@cygnus.com */
 
+#ifdef __STDC__
+struct frame_info;
+struct frame_saved_regs;
+struct value;
+struct type;
+#endif
+
 #define GDB_TARGET_IS_SH
 
 #define IEEE_FLOAT 1
@@ -109,13 +116,13 @@ extern CORE_ADDR sh_skip_prologue ();
    Entries beyond the first NUM_REGS are ignored.  */
 
 #define 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", \
+  { "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", \
+    "ssr",  "spc",   \
     "r0b0", "r1b0", "r2b0", "r3b0", "r4b0", "r5b0", "r6b0", "r7b0", \
     "r0b1", "r1b1", "r2b1", "r3b1", "r4b1", "r5b1", "r6b1", "r7b1", \
   }
@@ -130,6 +137,9 @@ extern CORE_ADDR sh_skip_prologue ();
    passed to read_register.  */
 
 #define R0_REGNUM      0
+#define STRUCT_RETURN_REGNUM 2
+#define ARG0_REGNUM     4
+#define ARGLAST_REGNUM  7
 #define FP_REGNUM      14
 #define SP_REGNUM      15
 #define PC_REGNUM      16
@@ -152,25 +162,28 @@ extern CORE_ADDR sh_skip_prologue ();
 /* Store the address of the place in which to copy the structure the
    subroutine will return.  This is called from call_function. 
 
-   We store structs through a pointer passed in R4 */
+   We store structs through a pointer passed in R0 */
 
 #define STORE_STRUCT_RETURN(ADDR, SP) \
-    { write_register (4, (ADDR));  }
+    { write_register (STRUCT_RETURN_REGNUM, (ADDR));  }
+
+#define USE_STRUCT_CONVENTION(gcc_p, type)     (TYPE_LENGTH(type) > 1)
 
 /* Extract from an array REGBUF containing the (raw) register state
    a function return value of type TYPE, and copy that, in virtual format,
    into VALBUF.  */
 
+extern void sh_extract_return_value PARAMS ((struct type *, void *, void *));
 #define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \
-  memcpy (VALBUF, (char *)(REGBUF), TYPE_LENGTH(TYPE))
+       sh_extract_return_value (TYPE, REGBUF, VALBUF)
 
 /* Write into appropriate registers a function return value
    of type TYPE, given in virtual format.  
 
-   Things always get returned in R4/R5 */
+   Things always get returned in R0/R1 */
 
 #define STORE_RETURN_VALUE(TYPE,VALBUF) \
-  write_register_bytes (REGISTER_BYTE(4), VALBUF, TYPE_LENGTH (TYPE))
+  write_register_bytes (REGISTER_BYTE(0), VALBUF, TYPE_LENGTH (TYPE))
 
 /* Extract from an array REGBUF containing the (raw) register state
    the address in which a function should return its structure value,
@@ -189,7 +202,7 @@ extern CORE_ADDR sh_skip_prologue ();
     int f_offset;    
 
 #define INIT_EXTRA_FRAME_INFO(fromleaf, fi) \
-    init_extra_frame_info(fromleaf, fi) 
+    sh_init_extra_frame_info(fromleaf, fi) 
 
 /* A macro that tells us whether the function invocation represented
    by FI does not have a frame on the stack associated with it.  If it
@@ -198,10 +211,9 @@ extern CORE_ADDR sh_skip_prologue ();
 #define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \
   (FRAMELESS) = frameless_look_for_prologue(FI)
 
-#define FRAME_CHAIN(FRAME)       sh_frame_chain(FRAME)
-#define FRAME_SAVED_PC(FRAME)    ((FRAME)->return_pc)
-#define FRAME_ARGS_ADDRESS(fi)   (fi)->frame
-#define FRAME_LOCALS_ADDRESS(fi) (fi)->frame
+#define FRAME_SAVED_PC(FRAME)          ((FRAME)->return_pc)
+#define FRAME_ARGS_ADDRESS(fi)         ((fi)->frame)
+#define FRAME_LOCALS_ADDRESS(fi)       ((fi)->frame)
 
 /* Set VAL to the number of args passed to frame described by FI.
    Can set VAL to -1, meaning no way to tell.  */
@@ -214,6 +226,9 @@ extern CORE_ADDR sh_skip_prologue ();
 
 #define FRAME_ARGS_SKIP 0
 
+extern void sh_frame_find_saved_regs PARAMS ((struct frame_info *fi, 
+                                             struct frame_saved_regs *fsr));
+
 /* Put here the code to store, into a struct frame_saved_regs,
    the addresses of the saved registers of frame described by FRAME_INFO.
    This includes special registers such as pc and fp saved in special
@@ -221,20 +236,57 @@ extern CORE_ADDR sh_skip_prologue ();
    the address we return for it IS the sp for the next frame.  */
 
 #define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs)        \
-   frame_find_saved_regs(frame_info, &(frame_saved_regs))
+   sh_frame_find_saved_regs(frame_info, &(frame_saved_regs))
 
 #define NAMES_HAVE_UNDERSCORE
 
 typedef unsigned short INSN_WORD;
 
-#define CALL_DUMMY_LENGTH 10
+extern CORE_ADDR generic_read_register_dummy PARAMS ((struct frame_info *, 
+                                                     int regno));
+
+extern void generic_push_dummy_frame    PARAMS ((void));
+extern void generic_pop_dummy_frame     PARAMS ((void));
+
+extern int  generic_pc_in_call_dummy    PARAMS ((CORE_ADDR pc, 
+                                                CORE_ADDR fp, 
+                                                CORE_ADDR sp));
+extern char * generic_find_dummy_frame  PARAMS ((CORE_ADDR pc, 
+                                                CORE_ADDR fp, 
+                                                CORE_ADDR sp));
+
+extern void sh_push_return_address PARAMS ((CORE_ADDR));
+extern CORE_ADDR sh_push_arguments PARAMS ((int nargs, 
+                                           struct value **args, 
+                                           CORE_ADDR sp,
+                                           unsigned char struct_return,
+                                           CORE_ADDR struct_addr));
+extern int generic_frame_chain_valid PARAMS((CORE_ADDR, struct frame_info *));
+
+
+
+#define CALL_DUMMY                   { }
+#define CALL_DUMMY_LENGTH            (0)
+#define CALL_DUMMY_START_OFFSET      (0)
+#define CALL_DUMMY_BREAKPOINT_OFFSET (0)
+#define CALL_DUMMY_LOCATION          AT_ENTRY_POINT
+#define CALL_DUMMY_ADDRESS()         (entry_point_address ())
+#define PUSH_RETURN_ADDRESS(PC)      (sh_push_return_address (PC))
+#define FRAME_CHAIN(FRAME)           (sh_frame_chain(FRAME))
+#define PUSH_DUMMY_FRAME             (generic_push_dummy_frame ())
+#define FRAME_CHAIN_VALID(FP, FRAME) (generic_frame_chain_valid (FP, FRAME))
+#define PC_IN_CALL_DUMMY(PC, SP, FP) (generic_pc_in_call_dummy (PC, SP, FP))
+#define FIX_CALL_DUMMY(DUMMYNAME, STARTADDR, FUNADDR, NARGS, ARGS, TYPE, GCCP) 
+#define PUSH_ARGUMENTS(NARGS, ARGS, SP, STRUCT_RETURN, STRUCT_ADDR) \
+  (SP) = sh_push_arguments (NARGS, ARGS, SP, STRUCT_RETURN, STRUCT_ADDR)
 
 /* Discard from the stack the innermost frame, restoring all saved
    registers.  */
 
-#define POP_FRAME pop_frame();
+#define POP_FRAME sh_pop_frame();
 
 #define NOP   {0x20, 0x0b}
 
 #define REGISTER_SIZE 4
 
+#define COERCE_FLOAT_TO_DOUBLE 1
index faeb60df5073ed95705e4073d3418fa93c9ee976..d8af915be439a64a7ab33babc6c382b26ff4d931 100644 (file)
@@ -31,6 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "gdbcore.h"
 #include "value.h"
 #include "dis-asm.h"
+#include "inferior.h"          /* for BEFORE_TEXT_END etc. */
 
 extern int remote_write_size;  /* in remote.c */
 
@@ -159,20 +160,54 @@ CORE_ADDR
 sh_frame_chain (frame)
      struct frame_info *frame;
 {
+  if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
+    return frame->frame;       /* dummy frame same as caller's frame */
   if (!inside_entry_file (frame->pc))
     return read_memory_integer (FRAME_FP (frame) + frame->f_offset, 4);
   else
     return 0;
 }
 
+/* Find REGNUM on the stack.  Otherwise, it's in an active register.  One thing
+   we might want to do here is to check REGNUM against the clobber mask, and
+   somehow flag it as invalid if it isn't saved on the stack somewhere.  This
+   would provide a graceful failure mode when trying to get the value of
+   caller-saves registers for an inner frame.  */
+
+CORE_ADDR
+sh_find_callers_reg (fi, regnum)
+     struct frame_info *fi;
+     int regnum;
+{
+  struct frame_saved_regs fsr;
+
+  for (; fi; fi = fi->next)
+    if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame))
+      /* When the caller requests PR from the dummy frame, we return PC because
+        that's where the previous routine appears to have done a call from. */
+      return generic_read_register_dummy (fi, regnum);
+    else 
+      {
+       FRAME_FIND_SAVED_REGS(fi, fsr);
+       if (fsr.regs[regnum] != 0)
+         return read_memory_integer (fsr.regs[regnum], 
+                                     REGISTER_RAW_SIZE(regnum));
+      }
+  return read_register (regnum);
+}
+
 /* Put here the code to store, into a struct frame_saved_regs, the
    addresses of the saved registers of frame described by FRAME_INFO.
    This includes special registers such as pc and fp saved in special
    ways in the stack frame.  sp is even more special: the address we
    return for it IS the sp for the next frame. */
 
+/* FIXME!  A lot of this should be abstracted out into a sh_scan_prologue 
+   function, and the struct frame_info should have a frame_saved_regs
+   embedded in it, so we would only have to do this once. */
+
 void
-frame_find_saved_regs (fi, fsr)
+sh_frame_find_saved_regs (fi, fsr)
      struct frame_info *fi;
      struct frame_saved_regs *fsr;
 {
@@ -184,6 +219,16 @@ frame_find_saved_regs (fi, fsr)
   int opc;
   int insn;
   int r3_val = 0;
+  char * dummy_regs = generic_find_dummy_frame (fi->pc, fi->frame, fi->frame);
+
+  if (dummy_regs)
+    {
+      /* DANGER!  This is ONLY going to work if the char buffer format of
+        the saved registers is byte-for-byte identical to the 
+        CORE_ADDR regs[NUM_REGS] format used by struct frame_saved_regs! */
+      memcpy (&fsr->regs, dummy_regs, sizeof(fsr));
+      return;
+    }
 
   opc = pc = get_pc_function_start (fi->pc);
 
@@ -276,55 +321,224 @@ frame_find_saved_regs (fi, fsr)
   fi->f_offset = depth - where[FP_REGNUM] - 4;
   /* Work out the return pc - either from the saved pr or the pr
      value */
-
-  if (fsr->regs[PR_REGNUM])
-    fi->return_pc = read_memory_integer (fsr->regs[PR_REGNUM], 4);
-  else
-    fi->return_pc = read_register (PR_REGNUM);
 }
 
 /* initialize the extra info saved in a FRAME */
 
 void
-init_extra_frame_info (fromleaf, fi)
+sh_init_extra_frame_info (fromleaf, fi)
      int fromleaf;
      struct frame_info *fi;
 {
-  struct frame_saved_regs dummy;
+  struct frame_saved_regs fsr;
 
   if (fi->next)
-    fi->pc = fi->next->return_pc;
+    fi->pc = FRAME_SAVED_PC (fi->next);
 
-  frame_find_saved_regs (fi, &dummy);
+  if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame))
+    {
+      /* We need to setup fi->frame here because run_stack_dummy gets it wrong
+        by assuming it's always FP.  */
+      fi->frame     = generic_read_register_dummy (fi, SP_REGNUM);
+      fi->return_pc = generic_read_register_dummy (fi, PC_REGNUM);
+      fi->f_offset = -(CALL_DUMMY_LENGTH + 4);
+      fi->leaf_function = 0;
+      return;
+    }
+  else
+    {
+      FRAME_FIND_SAVED_REGS (fi, fsr);
+      fi->return_pc = sh_find_callers_reg (fi, PR_REGNUM);
+    }
 }
 
-
 /* Discard from the stack the innermost frame,
    restoring all saved registers.  */
 
 void
-pop_frame ()
+sh_pop_frame ()
 {
   register struct frame_info *frame = get_current_frame ();
   register CORE_ADDR fp;
   register int regnum;
   struct frame_saved_regs fsr;
 
-  fp = FRAME_FP (frame);
-  get_frame_saved_regs (frame, &fsr);
+  if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
+    generic_pop_dummy_frame ();
+  else
+  {
+    fp = FRAME_FP (frame);
+    get_frame_saved_regs (frame, &fsr);
 
-  /* Copy regs from where they were saved in the frame */
-  for (regnum = 0; regnum < NUM_REGS; regnum++)
-    {
+    /* Copy regs from where they were saved in the frame */
+    for (regnum = 0; regnum < NUM_REGS; regnum++)
       if (fsr.regs[regnum])
+       write_register (regnum, read_memory_integer (fsr.regs[regnum], 4));
+
+    write_register (PC_REGNUM, frame->return_pc);
+    write_register (SP_REGNUM, fp + 4);
+  }
+  flush_cached_frames ();
+}
+
+/* Function: push_arguments
+   Setup the function arguments for calling a function in the inferior.
+
+   On the Hitachi SH architecture, there are four registers (R4 to R7)
+   which are dedicated for passing function arguments.  Up to the first
+   four arguments (depending on size) may go into these registers.
+   The rest go on the stack.
+
+   Arguments that are smaller than 4 bytes will still take up a whole
+   register or a whole 32-bit word on the stack, and will be 
+   right-justified in the register or the stack word.  This includes
+   chars, shorts, and small aggregate types.
+
+   Arguments that are larger than 4 bytes may be split between two or 
+   more registers.  If there are not enough registers free, an argument
+   may be passed partly in a register (or registers), and partly on the
+   stack.  This includes doubles, long longs, and larger aggregates. 
+   As far as I know, there is no upper limit to the size of aggregates 
+   that will be passed in this way; in other words, the convention of 
+   passing a pointer to a large aggregate instead of a copy is not used.
+
+   An exceptional case exists for struct arguments (and possibly other
+   aggregates such as arrays) if the size is larger than 4 bytes but 
+   not a multiple of 4 bytes.  In this case the argument is never split 
+   between the registers and the stack, but instead is copied in its
+   entirety onto the stack, AND also copied into as many registers as 
+   there is room for.  In other words, space in registers permitting, 
+   two copies of the same argument are passed in.  As far as I can tell,
+   only the one on the stack is used, although that may be a function 
+   of the level of compiler optimization.  I suspect this is a compiler
+   bug.  Arguments of these odd sizes are left-justified within the 
+   word (as opposed to arguments smaller than 4 bytes, which are 
+   right-justified).
+
+   If the function is to return an aggregate type such as a struct, it 
+   is either returned in the normal return value register R0 (if its 
+   size is no greater than one byte), or else the caller must allocate
+   space into which the callee will copy the return value (if the size
+   is greater than one byte).  In this case, a pointer to the return 
+   value location is passed into the callee in register R2, which does 
+   not displace any of the other arguments passed in via registers R4
+   to R7.   */
+
+CORE_ADDR
+sh_push_arguments (nargs, args, sp, struct_return, struct_addr)
+     int nargs;
+     value_ptr *args;
+     CORE_ADDR sp;
+     unsigned char struct_return;
+     CORE_ADDR struct_addr;
+{
+  int argreg;
+  int argnum;
+  CORE_ADDR regval;
+  char *val;
+  char valbuf[4];
+  int len;
+  int push[4];         /* some of the first 4 args may not need to be pushed
+                          onto the stack, because they can go in registers */
+
+  /* first force sp to a 4-byte alignment */
+  sp = sp & ~3;
+
+  /* The "struct return pointer" pseudo-argument has its own dedicated 
+     register */
+  if (struct_return)
+      write_register (STRUCT_RETURN_REGNUM, struct_addr);
+
+  /* Now load as many as possible of the first arguments into registers.
+     There are 16 bytes in four registers available. 
+     Loop thru args from first to last.  */
+  push[0] = push[1] = push[2] = push[3] = 0;
+  for (argnum = 0, argreg = ARG0_REGNUM; 
+       argnum < nargs && argreg <= ARGLAST_REGNUM; 
+       argnum++)
+    {
+      struct type *type = VALUE_TYPE (args[argnum]);
+      
+      len = TYPE_LENGTH (type);
+
+      switch (TYPE_CODE(type)) {
+      case TYPE_CODE_STRUCT:
+      case TYPE_CODE_UNION:
+      /* case TYPE_CODE_ARRAY:  case TYPE_CODE_STRING: */
+       if (len <= 4   ||   (len & ~3) == 0)
+         push[argnum] = 0;             /* doesn't get pushed onto stack */
+       else
+         push[argnum] = len;           /* does    get pushed onto stack */
+       break;
+      default:
+       push[argnum] = 0;               /* doesn't get pushed onto stack */
+      }
+      if (len < 4)
+       { /* value gets right-justified in the register */
+         memcpy(valbuf + (4 - len), 
+                (char *) VALUE_CONTENTS (args[argnum]), len);
+         val = valbuf;
+       }
+      else
+       val = (char *) VALUE_CONTENTS (args[argnum]);
+
+      while (len > 0)
        {
-         write_register (regnum, read_memory_integer (fsr.regs[regnum], 4));
+         regval = extract_address (val, REGISTER_RAW_SIZE (argreg));
+         write_register (argreg, regval);
+
+         len -= REGISTER_RAW_SIZE (argreg);
+         val += REGISTER_RAW_SIZE (argreg);
+         argreg++;
+         if (argreg > ARGLAST_REGNUM)
+           {
+             push[argnum] = len;       /* ran out of arg passing registers! */
+             break;                    /* len bytes remain to go onto stack */
+           }
        }
     }
 
-  write_register (PC_REGNUM, frame->return_pc);
-  write_register (SP_REGNUM, fp + 4);
-  flush_cached_frames ();
+  /* Now push as many as necessary of the remaining arguments onto the stack.
+     For args 0 to 3, the arg may have been passed in a register. 
+     Loop thru args from last to first.  */
+  for (argnum = nargs-1; argnum >= 0; --argnum)
+    {
+      if (argnum < 4 && push[argnum] == 0)
+       continue;       /* no need to push this arg */
+
+      len = TYPE_LENGTH (VALUE_TYPE (args[argnum]));
+      if (len < 4)
+       {
+         memcpy(valbuf + (4 - len), 
+                (char *) VALUE_CONTENTS (args[argnum]), len);
+         val = valbuf;
+       }
+      else
+       val = (char *) VALUE_CONTENTS (args[argnum]);
+
+      if (argnum < 4)
+       if (len > push[argnum])         /* some part may already be in a reg */
+         {
+           val += (len - push[argnum]);
+           len = push[argnum];
+         }
+
+      sp -= (len + 3) & ~3;
+      write_memory (sp, val, len);
+    }
+  return sp;
+}
+
+/* Function: push_return_address (pc)
+   Set up the return address for the inferior function call.
+   Necessary for targets where we don't actually execute a JSR/BSR instruction */
+
+void
+sh_push_return_address (pc)
+     CORE_ADDR pc;
+{
+  write_register (PR_REGNUM, entry_point_address ());
 }
 
 /* Command to set the processor type.  */
@@ -400,7 +614,7 @@ sh_set_processor_type (str)
 /* Print the registers in a form similar to the E7000 */
 
 static void
-show_regs (args, from_tty)
+sh_show_regs (args, from_tty)
      char *args;
      int from_tty;
 {
@@ -430,7 +644,23 @@ show_regs (args, from_tty)
                   read_register (14),
                   read_register (15));
 }
-\f
+
+void
+sh_extract_return_value (type, regbuf, valbuf)
+     struct type *type;
+     void *regbuf;
+     void *valbuf;
+{
+  int len = TYPE_LENGTH(type);
+
+  if (len <= 4)
+    memcpy (valbuf, ((char *) regbuf) + 4 - len, len);
+  else if (len <= 8)
+    memcpy (valbuf, ((char *) regbuf) + 8 - len, len);
+  else
+    error ("bad size for return value");
+}
+
 void
 _initialize_sh_tdep ()
 {
@@ -451,9 +681,199 @@ Set this to be able to access processor-type-specific registers.\n\
   tmp_sh_processor_type = strsave (DEFAULT_SH_TYPE);
   sh_set_processor_type_command (strsave (DEFAULT_SH_TYPE), 0);
 
-  add_com ("regs", class_vars, show_regs, "Print all registers");
+  add_com ("regs", class_vars, sh_show_regs, "Print all registers");
 
   /* Reduce the remote write size because some CMONs can't take
     more than 400 bytes in a packet.  300 seems like a safe bet.  */
   remote_write_size = 300;
 }
+
+/*
+ * DUMMY FRAMES
+ * 
+ * The following code serves to maintain the dummy stack frames for
+ * inferior function calls (ie. when gdb calls into the inferior via
+ * call_function_by_hand).  This code saves the machine state before 
+ * the call in host memory, so it must maintain an independant stack 
+ * and keep it consistant etc.  I am attempting to make this code 
+ * generic enough to be used by many targets.
+ *
+ * The cheapest and most generic way to do CALL_DUMMY on a new target
+ * is probably to define CALL_DUMMY to be empty, CALL_DUMMY_LENGTH to zero,
+ * and CALL_DUMMY_LOCATION to AT_ENTRY.  Then you must remember to define
+ * PUSH_RETURN_ADDRESS, because there won't be a call instruction to do it.
+ */
+
+/* Dummy frame.  This saves the processor state just prior to setting up the
+   inferior function call.  On most targets, the registers are saved on the
+   target stack, but that really slows down function calls.  */
+
+struct dummy_frame
+{
+  struct dummy_frame *next;
+
+  CORE_ADDR pc;
+  CORE_ADDR fp;
+  CORE_ADDR sp;
+  char regs[REGISTER_BYTES];
+};
+
+static struct dummy_frame *dummy_frame_stack = NULL;
+
+/* Function: find_dummy_frame(pc, fp, sp)
+   Search the stack of dummy frames for one matching the given PC, FP and SP.
+   This is the work-horse for pc_in_call_dummy and read_register_dummy     */
+
+char * 
+generic_find_dummy_frame (pc, fp, sp)
+     CORE_ADDR pc;
+     CORE_ADDR fp;
+     CORE_ADDR sp;
+{
+  struct dummy_frame * dummyframe;
+  CORE_ADDR bkpt_address;
+  extern CORE_ADDR text_end;
+
+#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
+  bkpt_address = entry_point_address () + CALL_DUMMY_BREAKPOINT_OFFSET;
+  if (pc != bkpt_address &&
+      pc != bkpt_address + DECR_PC_AFTER_BREAK)
+    return 0;
+#endif /* AT_ENTRY_POINT */
+
+#if CALL_DUMMY_LOCATION == BEFORE_TEXT_END
+  bkpt_address = text_end - CALL_DUMMY_LENGTH + CALL_DUMMY_BREAKPOINT_OFFSET;
+  if (pc != bkpt_address &&
+      pc != bkpt_address + DECR_PC_AFTER_BREAK)
+    return 0;
+#endif /* BEFORE_TEXT_END */
+
+#if CALL_DUMMY_LOCATION == AFTER_TEXT_END
+  bkpt_address = text_end + CALL_DUMMY_BREAKPOINT_OFFSET;
+  if (pc != bkpt_address &&
+      pc != bkpt_address + DECR_PC_AFTER_BREAK)
+    return 0;
+#endif /* AFTER_TEXT_END */
+
+  for (dummyframe = dummy_frame_stack;
+       dummyframe;
+       dummyframe = dummyframe->next)
+    if (fp == dummyframe->fp || 
+       sp == dummyframe->sp)
+      {
+#if CALL_DUMMY_LOCATION == ON_STACK
+       CORE_ADDR bkpt_offset;  /* distance from original frame ptr to bkpt */
+
+       if (1 INNER_THAN 2)
+         bkpt_offset = CALL_DUMMY_BREAK_OFFSET;
+       else
+         bkpt_offset = CALL_DUMMY_LENGTH - CALL_DUMMY_BREAK_OFFSET;
+
+       if (pc + bkpt_offset == dummyframe->fp ||
+           pc + bkpt_offset == dummyframe->sp ||
+           pc + bkpt_offset + DECR_PC_AFTER_BREAK == dummyframe->fp ||
+           pc + bkpt_offset + DECR_PC_AFTER_BREAK == dummyframe->sp)
+#endif /* ON_STACK */
+         return dummyframe->regs;
+      }
+  return 0;
+}
+
+/* Function: pc_in_call_dummy (pc, fp, sp)
+   Return true if this is a dummy frame created by gdb for an inferior call */
+
+int
+generic_pc_in_call_dummy (pc, fp, sp)
+     CORE_ADDR pc;
+     CORE_ADDR fp;
+     CORE_ADDR sp;
+{
+  /* if find_dummy_frame succeeds, then PC is in a call dummy */
+  return (generic_find_dummy_frame (pc, fp, sp) != 0);
+}
+
+/* Function: read_register_dummy (pc, fp, sp, regno)
+   Find a saved register from before GDB calls a function in the inferior */
+
+CORE_ADDR
+generic_read_register_dummy (fi, regno)
+     struct frame_info *fi;
+     int regno;
+{
+  char *dummy_regs = generic_find_dummy_frame (fi->pc, fi->frame, NULL);
+
+  if (dummy_regs)
+    return extract_address (&dummy_regs[REGISTER_BYTE (regno)],
+                           REGISTER_RAW_SIZE(regno));
+  else
+    return 0;
+}
+
+/* Save all the registers on the dummy frame stack.  Most ports save the
+   registers on the target stack.  This results in lots of unnecessary memory
+   references, which are slow when debugging via a serial line.  Instead, we
+   save all the registers internally, and never write them to the stack.  The
+   registers get restored when the called function returns to the entry point,
+   where a breakpoint is laying in wait.  */
+
+void
+generic_push_dummy_frame ()
+{
+  struct dummy_frame *dummy_frame;
+  CORE_ADDR fp = read_register(FP_REGNUM);
+
+  /* check to see if there are stale dummy frames, 
+     perhaps left over from when a longjump took us out of a 
+     function that was called by the debugger */
+
+  dummy_frame = dummy_frame_stack;
+  while (dummy_frame)
+    if (dummy_frame->fp INNER_THAN fp) /* stale -- destroy! */
+      {
+       dummy_frame_stack = dummy_frame->next;
+       free (dummy_frame);
+       dummy_frame = dummy_frame_stack;
+      }
+    else
+      dummy_frame = dummy_frame->next;
+
+  dummy_frame = xmalloc (sizeof (struct dummy_frame));
+
+  read_register_bytes (0, dummy_frame->regs, REGISTER_BYTES);
+  dummy_frame->pc   = read_register (PC_REGNUM);
+  dummy_frame->fp   = read_register (FP_REGNUM);
+  dummy_frame->sp   = read_register (SP_REGNUM);
+  dummy_frame->next = dummy_frame_stack;
+  dummy_frame_stack = dummy_frame;
+}
+
+/* Function: pop_dummy_frame
+   Restore the machine state from a saved dummy stack frame. */
+
+void
+generic_pop_dummy_frame ()
+{
+  struct dummy_frame *dummy_frame = dummy_frame_stack;
+
+  if (!dummy_frame)
+    error ("Can't pop dummy frame!");
+  dummy_frame_stack = dummy_frame->next;
+  write_register_bytes (0, dummy_frame->regs, REGISTER_BYTES);
+  free (dummy_frame);
+}
+
+/* Function: frame_chain_valid 
+   Returns true for a user frame or a call_function_by_hand dummy frame,
+   and false for the CRT0 start-up frame.  Purpose is to terminate backtrace */
+
+int
+generic_frame_chain_valid (fp, fi)
+     CORE_ADDR fp;
+     struct frame_info *fi;
+{
+  if (PC_IN_CALL_DUMMY(FRAME_SAVED_PC(fi), fp, fp))
+    return 1;  /* don't prune CALL_DUMMY frames */
+  else         /* fall back to default algorithm (see frame.h) */
+    return (fp != 0 && !inside_entry_file (FRAME_SAVED_PC(fi)));
+}
+