+static CORE_ADDR
+d10v_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ ULONGEST pc;
+ frame_unwind_unsigned_register (next_frame, PC_REGNUM, &pc);
+ return d10v_make_iaddr (pc);
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+ frame. This will be used to create a new GDB frame struct. */
+
+static void
+d10v_frame_this_id (struct frame_info *next_frame,
+ void **this_prologue_cache,
+ struct frame_id *this_id)
+{
+ struct d10v_unwind_cache *info
+ = d10v_frame_unwind_cache (next_frame, this_prologue_cache);
+ CORE_ADDR base;
+ CORE_ADDR func;
+ struct frame_id id;
+
+ /* The FUNC is easy. */
+ func = frame_func_unwind (next_frame);
+
+ /* This is meant to halt the backtrace at "_start". Make sure we
+ don't halt it at a generic dummy frame. */
+ if (func <= IMEM_START || inside_entry_file (func))
+ return;
+
+ /* Hopefully the prologue analysis either correctly determined the
+ frame's base (which is the SP from the previous frame), or set
+ that base to "NULL". */
+ base = info->prev_sp;
+ if (base == STACK_START || base == 0)
+ return;
+
+ id = frame_id_build (base, func);
+
+ /* Check that we're not going round in circles with the same frame
+ ID (but avoid applying the test to sentinel frames which do go
+ round in circles). Can't use frame_id_eq() as that doesn't yet
+ compare the frame's PC value. */
+ if (frame_relative_level (next_frame) >= 0
+ && get_frame_type (next_frame) != DUMMY_FRAME
+ && frame_id_eq (get_frame_id (next_frame), id))
+ return;
+
+ (*this_id) = id;
+}
+
+static void
+saved_regs_unwinder (struct frame_info *next_frame,
+ CORE_ADDR *this_saved_regs,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *bufferp)
+{
+ if (this_saved_regs[regnum] != 0)
+ {
+ if (regnum == D10V_SP_REGNUM)
+ {
+ /* SP register treated specially. */
+ *optimizedp = 0;
+ *lvalp = not_lval;
+ *addrp = 0;
+ *realnump = -1;
+ if (bufferp != NULL)
+ store_unsigned_integer (bufferp,
+ register_size (current_gdbarch, regnum),
+ this_saved_regs[regnum]);
+ }
+ else
+ {
+ /* Any other register is saved in memory, fetch it but cache
+ a local copy of its value. */
+ *optimizedp = 0;
+ *lvalp = lval_memory;
+ *addrp = this_saved_regs[regnum];
+ *realnump = -1;
+ if (bufferp != NULL)
+ {
+ /* Read the value in from memory. */
+ read_memory (this_saved_regs[regnum], bufferp,
+ register_size (current_gdbarch, regnum));
+ }
+ }
+ return;
+ }
+
+ /* No luck, assume this and the next frame have the same register
+ value. If a value is needed, pass the request on down the chain;
+ otherwise just return an indication that the value is in the same
+ register as the next frame. */
+ frame_register_unwind (next_frame, regnum, optimizedp, lvalp, addrp,
+ realnump, bufferp);
+}
+
+
+static void
+d10v_frame_prev_register (struct frame_info *next_frame,
+ void **this_prologue_cache,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *bufferp)
+{
+ struct d10v_unwind_cache *info
+ = d10v_frame_unwind_cache (next_frame, this_prologue_cache);
+ if (regnum == PC_REGNUM)
+ {
+ /* The call instruction saves the caller's PC in LR. The
+ function prologue of the callee may then save the LR on the
+ stack. Find that possibly saved LR value and return it. */
+ saved_regs_unwinder (next_frame, info->saved_regs, LR_REGNUM, optimizedp,
+ lvalp, addrp, realnump, bufferp);
+ }
+ else
+ {
+ saved_regs_unwinder (next_frame, info->saved_regs, regnum, optimizedp,
+ lvalp, addrp, realnump, bufferp);
+ }
+}
+
+static const struct frame_unwind d10v_frame_unwind = {
+ NORMAL_FRAME,
+ d10v_frame_this_id,
+ d10v_frame_prev_register
+};
+
+const struct frame_unwind *
+d10v_frame_p (CORE_ADDR pc)
+{
+ return &d10v_frame_unwind;
+}
+
+static CORE_ADDR
+d10v_frame_base_address (struct frame_info *next_frame, void **this_cache)
+{
+ struct d10v_unwind_cache *info
+ = d10v_frame_unwind_cache (next_frame, this_cache);
+ return info->base;
+}
+
+static const struct frame_base d10v_frame_base = {
+ &d10v_frame_unwind,
+ d10v_frame_base_address,
+ d10v_frame_base_address,
+ d10v_frame_base_address
+};
+
+/* Assuming NEXT_FRAME->prev is a dummy, return the frame ID of that
+ dummy frame. The frame ID's base needs to match the TOS value
+ saved by save_dummy_frame_tos(), and the PC match the dummy frame's
+ breakpoint. */
+
+static struct frame_id
+d10v_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ ULONGEST base;
+ frame_unwind_unsigned_register (next_frame, D10V_SP_REGNUM, &base);
+ return frame_id_build (d10v_make_daddr (base), frame_pc_unwind (next_frame));
+}