/* Get info from stack frames;
   convert between frames, blocks, functions and pc values.
   Copyright (C) 1986, 1987 Free Software Foundation, Inc.

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 "defs.h"
#include "initialize.h"
#include "param.h"
#include "symtab.h"
#include "frame.h"

/* Address of end of first object file.
   This file is assumed to be a startup file
   and frames with pc's inside it
   are treated as nonexistent.  */

CORE_ADDR first_object_file_end;

/* Address of innermost stack frame (contents of FP register) */

static FRAME current_frame;

struct block *block_for_pc ();
CORE_ADDR get_pc_function_start ();

START_FILE

/* Return the innermost (currently executing) stack frame.  */

FRAME
get_current_frame ()
{
  /* We assume its address is kept in a general register;
     param.h says which register.  */

  return current_frame;
}

void
set_current_frame (frame)
     FRAME frame;
{
  current_frame = frame;
}

/* Return the frame that called FRAME.
   If FRAME is the original frame (it has no caller), return 0.  */

FRAME
get_prev_frame (frame)
     FRAME frame;
{
  CORE_ADDR pointer;
  /* The caller of "no frame" is the innermost frame.  */
  if (frame == 0)
    return get_current_frame ();

  /* Two macros defined in param.h specify the machine-dependent
     actions to be performed here.  */
  /* First, get the frame's chain-pointer.
     If that is zero, the frame is the outermost frame.  */
  pointer = FRAME_CHAIN (frame);
  if (!FRAME_CHAIN_VALID (pointer, frame))
    return 0;
  /* If frame has a caller, combine the chain pointer and the frame's own
     address to get the address of the caller.  */
  return FRAME_CHAIN_COMBINE (pointer, frame);
}

/* Return a structure containing various interesting information
   about a specified stack frame.  */

struct frame_info
get_frame_info (frame)
     FRAME frame;
{
  struct frame_info val;
  FRAME current = get_current_frame ();
  register FRAME frame1;

  val.frame = frame;

  if (frame == current)
    {
      val.pc = read_pc ();
      val.next_frame = 0;
    }
  else 
    {
      for (frame1 = current; frame1; frame1 = get_prev_frame (frame1))
	{
	  QUIT;
	  if (frame1 == frame)
	    break;

	  val.pc = FRAME_SAVED_PC (frame1);
	  val.next_frame = frame1;
	}
    }

  return val;
}

/* Return a structure containing various interesting information
   about the frame that called FRAME.

   This is much faster than get_frame_info (get_prev_frame (FRAME))
   because it does not need to search the entire stack
   to find the frame called by the one being described -- that is FRAME.  */

struct frame_info
get_prev_frame_info (next_frame)
     FRAME next_frame;
{
  struct frame_info val;
  register FRAME frame = get_prev_frame (next_frame);

  val.frame = frame;
  val.next_frame = next_frame;

  if (next_frame == 0)
    {
      val.pc = read_pc ();
    }
  else 
    {
      val.pc = FRAME_SAVED_PC (next_frame);
    }

  return val;
}

CORE_ADDR
get_frame_pc (frame)
     FRAME frame;
{
  struct frame_info fi;
  fi = get_frame_info (frame);
  return fi.pc;
}

/* Find the addresses in which registers are saved in FRAME.  */

void
get_frame_saved_regs (frame_info_addr, saved_regs_addr)
     struct frame_info *frame_info_addr;
     struct frame_saved_regs *saved_regs_addr;
{
  FRAME_FIND_SAVED_REGS (*frame_info_addr, *saved_regs_addr);
}

/* Return the innermost lexical block in execution
   in a specified stack frame.  The frame address is assumed valid.  */

struct block *
get_frame_block (frame)
     FRAME frame;
{
  struct frame_info fi;

  fi = get_frame_info (frame);
  return block_for_pc (fi.pc);
}

struct block *
get_current_block ()
{
  return block_for_pc (read_pc ());
}

CORE_ADDR
get_pc_function_start (pc)
     CORE_ADDR pc;
{
  register struct block *bl = block_for_pc (pc);
  register struct symbol *symbol;
  if (bl == 0)
    {
      register int misc_index = find_pc_misc_function (pc);
      if (misc_index >= 0)
	return misc_function_vector[misc_index].address;
      return 0;
    }
  symbol = block_function (bl);
  bl = SYMBOL_BLOCK_VALUE (symbol);
  return BLOCK_START (bl);
}  

/* Return the symbol for the function executing in frame FRAME.  */

struct symbol *
get_frame_function (frame)
     FRAME frame;
{
  register struct block *bl = get_frame_block (frame);
  if (bl == 0)
    return 0;
  return block_function (bl);
}

/* Return the innermost lexical block containing the specified pc value,
   or 0 if there is none.  */

struct block *
block_for_pc (pc)
     register CORE_ADDR pc;
{
  register struct block *b;
  register int bot, top, half;
  register struct symtab *s;
  struct blockvector *bl;

  /* First search all symtabs for one whose file contains our pc */

  for (s = symtab_list; s; s = s->next)
    {
      bl = BLOCKVECTOR (s);
      b = BLOCKVECTOR_BLOCK (bl, 0);
      if (BLOCK_START (b) <= pc
	  && BLOCK_END (b) > pc)
	break;
    }

  if (s == 0)
    return 0;

  /* Then search that symtab for the smallest block that wins.  */
  /* Use binary search to find the last block that starts before PC.  */

  bot = 0;
  top = BLOCKVECTOR_NBLOCKS (bl);

  while (top - bot > 1)
    {
      half = (top - bot + 1) >> 1;
      b = BLOCKVECTOR_BLOCK (bl, bot + half);
      if (BLOCK_START (b) <= pc)
	bot += half;
      else
	top = bot + half;
    }

  /* Now search backward for a block that ends after PC.  */

  while (bot >= 0)
    {
      b = BLOCKVECTOR_BLOCK (bl, bot);
      if (BLOCK_END (b) > pc)
	return b;
      bot--;
    }

  return 0;
}

/* Return the function containing pc value PC.
   Returns 0 if function is not known.  */

struct symbol *
find_pc_function (pc)
     CORE_ADDR pc;
{
  register struct block *b = block_for_pc (pc);
  if (b == 0)
    return 0;
  return block_function (b);
}

/* Find the misc function whose address is the largest
   while being less than PC.  Return its index in misc_function_vector.
   Returns -1 if PC is not in suitable range.  */

int
find_pc_misc_function (pc)
     CORE_ADDR pc;
{
  register int i;

  /* Note that the last thing in the vector is always _etext.  */
  for (i = 0; i < misc_function_count; i++)
    {
      if (pc < misc_function_vector[i].address)
	return i - 1;
    }
  return -1;
}

/* Return the innermost stack frame executing inside of the specified block,
   or zero if there is no such frame.  */

FRAME
block_innermost_frame (block)
     struct block *block;
{
  struct frame_info fi;
  register FRAME frame;
  register CORE_ADDR start = BLOCK_START (block);
  register CORE_ADDR end = BLOCK_END (block);

  frame = 0;
  while (1)
    {
      fi = get_prev_frame_info (frame);
      frame = fi.frame;
      if (frame == 0)
	return 0;
      if (fi.pc >= start && fi.pc < end)
	return frame;
    }
}

static
initialize ()
{
}

END_FILE
