This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[RFA] New register definition interface


This patch introduces a new interface for defining registers in GDB.  It
applies over the two regcache.c patches I posted earlier.

The patch does the following:

  1. Implements platform-independent versions of these functions:

       gdbarch_num_regs
       gdbarch_num_pseudo_regs
       gdbarch_register_name
       gdbarch_register_bytes
       gdbarch_register_byte
       gdbarch_register_raw_size
       gdbarch_max_register_raw_size
       gdbarch_register_virtual_size
       gdbarch_max_register_virtual_size
       gdbarch_register_virtual_type
       gdbarch_fetch_pseudo_register
       gdbarch_store_pseudo_register
       gdbarch_get_saved_register
       gdbarch_pop_frame
       gdbarch_frame_saved_pc
       gdbarch_frame_chain
       gdbarch_do_registers_info

  2. As a consequence of (1), switches register sets when switching
     gdbarchs.

  3. Supports memory-mapped registers.  If a GDB user writes to the
     inferior's memory at a location where a register is mapped, the user
     sees the new value when examining the register.

  4. Makes it easier to define pseudo-registers.  To define a
     pseudo-register, an architecture specifies a list of parent registers
     and callbacks to read and write the pseudo-register.  Common code
     handles invalidating a pseudo-register when changing a parent
     register on which the pseudo-register depends.

     For example, the read callback for implementing alias registers looks
     like this:

       regs_rpseudo_alias (struct frame_info *frame, int regnum, int *deps,
                           void *data, char *rawbuf)
       {
         return regs_fetch_frame (frame, *deps, rawbuf);
       }

  5. Automatically displays registers in multiple columns.

  6. Introduces a new mechanism for finding register values in stack
     frames.  It allows the target to specify calculated values (e.g.
     previous sp is current sp minus frame size) as well as values saved
     on the stack.

     This supports the generic gdbarch_pop_frame, gdbarch_frame_saved_pc,
     and gdbarch_frame_chain implementations.  It also will make it easier
     to add an infrastructure for debugging framepointerless frames, in
     which sp and other register values can only be found by limited
     machine code emulation.

  7. Looks up register names using an O(log(n)) rather than O(n)
     algorithm.

  8. Provides support for decoupling target number from debug info
     number.  To do: audit GDB for debug info number references and change
     them to e.g. REGISTER_DNUM(regnum).

Here's an example of how the interface might be used for architecture
"foo" with the following registers:

    r0..r31   32-bit data registers
    sp        stack pointer
    ret       return value register, alias for r0

  static void
  foo_init_regs (struct gdbarch *gdbarch)
  {
    struct regs_init_context *context;
    char name[4];
    int i, parents[2];

    context = regs_init_start (gdbarch, foo_caller_regs);

    /* General data registers.  */
    for (i = 0; i < 32; i++)
      {
        sprintf (name, "r%d", i);
        regs_init_real (context, name, i, 4, &builtin_type_int32, 0);
      }

    /* Stack pointer.  */
    regs_init_real (context, "sp", FOO_SP_REGNUM, 4, &builtin_type_CORE_ADDR, 0);

    /* Return value.  */
    parents[0] = 0;
    parents[1] = -1;
    regs_init_pseudo (context, "ret", 4, &builtin_type_int32, REGS_HIDEALL,
                      regs_rpseudo_alias, regs_wpseudo_alias, NULL, parents);

    regs_init_finish (context);
  }

ChangeLog:

	* gdbarch.sh (REGISTER_LIST): New variable.
	* Makefile.in (SFILES): Add regs.c and cli/cli-regs.c.
	(COMMON_OBS): Add regs.o and cli/cli-regs.o.
	(regs.o, cli/cli-regs.o): New rules.
	* parse.c: Include regs.h.
	(target_map_name_to_register): Call regs_name_tnum() if
	available.
	* regcache.c: Include regs.h.
	(register_cached): Call regs_valid() if available.
	(set_register_cached): Call regs_set_valid() if available.
	(register_buffer): Call regs_buffer() if available.
	(real_register): Call regs_real() if available.
	(pseudo_register): Call regs_pseudo() if available.
	(build_regcache): Omit initialization if REGISTER_LIST.
	* regs.c: New file.
	* regs.h: New file.
	* cli/cli-regs.c: New file.
	* target.c: Include regs.h.
	(target_xfer_memory): Call regs_memwrite().
	* cli/cli-regs.h: 
	* gdbarch.c: Regenerate.
	* gdbarch.h: Regenerate.

At the moment, I'm not posting any architecture-specific patches to use
this interface.  It should coexist happily with the existing interface,
however.

There are no regressions on i686-pc-linux-gnu.

Comments?  Okay to apply?

Nicholas Duffek
<nsd@redhat.com>

[patch follows]

Index: gdb/Makefile.in
===================================================================
diff -up gdb/Makefile.in gdb/Makefile.in
--- gdb/Makefile.in	Wed Jan  3 15:36:55 2001
+++ gdb/Makefile.in	Tue Jan  2 22:49:48 2001
@@ -141,9 +141,10 @@ INTL_CFLAGS = -I$(INTL_DIR) -I$(INTL_SRC
 # CLI sub directory definitons
 #
 SUBDIR_CLI_OBS = \
-	cli-decode.o cli-script.o cli-cmds.o cli-setshow.o
+	cli-decode.o cli-script.o cli-cmds.o cli-setshow.o cli-regs.o
 SUBDIR_CLI_SRCS = \
-	cli/cli-decode.c cli/cli-script.c cli/cli-cmds.c cli/cli-setshow.c
+	cli/cli-decode.c cli/cli-script.c cli/cli-cmds.c cli/cli-setshow.c \
+	cli/cli-regs.c
 SUBDIR_CLI_DEPS =
 SUBDIR_CLI_INITS =
 SUBDIR_CLI_LDFLAGS=
@@ -514,6 +515,7 @@ SFILES = ax-general.c ax-gdb.c bcache.c 
 	event-loop.c event-top.c \
 	expprint.c f-exp.y f-lang.c f-typeprint.c f-valprint.c \
 	findvar.c regcache.c gdbarch.c arch-utils.c gdbtypes.c \
+	regs.c \
 	inf-loop.c infcmd.c inflow.c infrun.c language.c \
 	kod.c kod-cisco.c \
 	ui-out.c cli-out.c \
@@ -599,11 +601,13 @@ version_h = 	version.h
 ui_out_h =      ui-out.h
 cli_out_h =	cli-out.h
 arch_utils_h = arch-utils.h
+regs_h =	regs.h
 
 cli_decode_h =	$(srcdir)/cli/cli-decode.h
 cli_cmds_h =	$(srcdir)/cli/cli-cmds.h
 cli_script_h =	$(srcdir)/cli/cli-script.h
 cli_setshow_h =	$(srcdir)/cli/cli-setshow.h
+cli_regs_h =	$(srcdir)/cli/cli-regs.h
 
 # Header files that need to have srcdir added.  Note that in the cases
 # where we use a macro like $(gdbcmd_h), things are carefully arranged
@@ -657,6 +661,7 @@ TAGFILES_NO_SRCDIR = $(SFILES) $(HFILES_
 TAGFILES_WITH_SRCDIR = $(HFILES_WITH_SRCDIR)
 
 COMMON_OBS = version.o blockframe.o breakpoint.o findvar.o regcache.o \
+	regs.o \
 	source.o values.o eval.o valops.o valarith.o valprint.o printcmd.o \
 	symtab.o symfile.o symmisc.o linespec.o infcmd.o infrun.o \
 	expprint.o environ.o stack.o thread.o \
@@ -1339,7 +1344,10 @@ expprint.o: expprint.c $(defs_h) $(expre
 findvar.o: findvar.c $(defs_h) $(gdbcore_h) $(inferior_h) target.h \
 	gdb_string.h
 
-regcache.o: regcache.c $(defs_h) $(inferior_h) target.h 
+regcache.o: regcache.c $(defs_h) $(inferior_h) target.h $(regs_h)
+
+regs.o: regs.c $(defs_h) $(inferior_h) $(arch_utils_h) \
+	$(gdbcore_h) $(symfile_h) $(regs_h)
 
 fork-child.o: fork-child.c gdb_wait.h $(defs_h) $(gdbcore_h) \
 	$(inferior_h) target.h terminal.h gdbthread.h gdb_string.h
@@ -1691,7 +1699,7 @@ hp-symtab-read.o: hp-symtab-read.c hprea
 
 parse.o: parse.c $(command_h) $(defs_h) $(expression_h) $(frame_h) \
 	$(gdbtypes_h) language.h parser-defs.h $(symtab_h) $(value_h) \
-	gdb_string.h linespec.h
+	gdb_string.h linespec.h $(regs_h)
 
 ppc-bdm.o: ppc-bdm.c $(defs_h) $(gdbcore_h) gdb_string.h $(frame_h) \
 	$(inferior_h) $(bfd_h) symfile.h target.h gdb_wait.h $(gdbcmd_h) \
@@ -1948,7 +1956,7 @@ linespec.o: linespec.c linespec.h $(defs
 tic80-tdep.o: tic80-tdep.c $(defs_h)
 
 target.o: target.c $(bfd_h) $(defs_h) $(gdbcmd_h) $(inferior_h) \
-	objfiles.h symfile.h target.h gdb_string.h
+	objfiles.h symfile.h target.h gdb_string.h $(regs_h)
 
 thread.o: thread.c $(defs_h) gdbthread.h $(gdbcmd_h) target.h
 
@@ -2078,6 +2086,11 @@ cli-script.o: $(srcdir)/cli/cli-script.c
 		$(cli_cmds_h) $(cli_decode_h) top.h \
 		$(defs_h) $(value_h) language.h $(ui_out_h)
 	$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/cli/cli-script.c
+
+cli-regs.o: $(srcdir)/cli/cli-regs.c $(cli_regs_h) \
+		$(defs_h) $(floatformat_h) $(frame_h) $(value_h) \
+		$(regs_h) ui-file.h $(INCLUDE_DIR)/symcat.h
+	$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/cli/cli-regs.c
 
 #
 # MI dependencies
Index: gdb/cli/cli-regs.c
===================================================================
diff -up /dev/null gdb/cli/cli-regs.c
--- /dev/null	Sun Feb 12 03:29:56 1995
+++ gdb/cli/cli-regs.c	Wed Jan  3 15:08:48 2001
@@ -0,0 +1,470 @@
+/* GDB CLI register display library.
+   Copyright 2000 Free Software Foundation, Inc.
+
+   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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include "defs.h"
+#include "floatformat.h"
+#include "ui-file.h"
+#include "symcat.h"
+#include "regs.h"
+#include "value.h"
+#include "frame.h"
+
+/* Minimum space between register name and values.  */
+
+#define NAMEGAP 2
+
+/* Minimum space between register hex and decimal values.  */
+
+#define VALGAP 1
+
+/* Minimum space between columns.  */
+
+#define COLGAP 4
+
+/* Space between vector elements.  */
+
+#define VECGAP 2
+
+/* Screen width.  Should query terminal, but this works for a first cut.  */
+
+#define SCREENW 80
+
+/* Strings to display when register values are unavailable for various
+   reasons.  */
+
+#define DISP_INVFLOAT	"<invalid float>"
+#define DISP_NOVAL	"<unavailable>"
+#define DISP_EFFECT	"<side effect>"
+
+/* Indices into 2-element arrays representing "info all-registers" and "info
+   registers" formatting information.  */
+
+#define SOME 0
+#define ALL 1
+
+/* Per-format information.  */
+
+struct fmt
+  {
+    int namepad;		/* number of spaces after the name */
+    int valstart;		/* column at which values are printed */
+    int newline;		/* whether a newline instead of a column gap
+				   should follow this register */
+  };
+
+/* Per-register information.  */
+
+struct reg
+  {
+    int width1;			/* number of columns to use when displaying
+				   this register's first value */
+    int width2;			/* number of columns to use when displaying
+				   this register's second value */
+    struct fmt fmt[2];		/* format info for "info all-registers" and
+				   "info registers" */
+  };
+
+/* Per-architecture information for internal use by this module.  */
+
+struct inf
+  {
+    struct reg *regs;		/* per-register information */
+    char *virtbuf;		/* for storing any virtual register value */
+  };
+
+/* Identifier returned by register_gdbarch_data().  */
+
+static struct gdbarch_data *inf_id;
+
+/* Print the TYPE value in virtual BUF using val_print() FORMAT, right-padding
+   it to WIDTH characters if RIGHT and left-padding it otherwise.  */
+
+static void
+printval (struct type *type, char *buf, int format, int right, int width)
+{
+  static struct ui_file *out = NULL;
+  static char *bufstr = NULL;
+  long len;
+
+  if (!out)
+    out = mem_fileopen ();
+  else
+    ui_file_rewind (out);
+
+  val_print (type, buf, 0, 0, out, format, 1, 0, Val_pretty_default);
+
+  /* printf_filtered() may return nonlocally, so allocate bufstr statically
+     to avoid memory leaks.  */
+  if (bufstr)
+    free (bufstr);
+  bufstr = ui_file_xstrdup (out, &len);
+
+  printf_filtered (right ? "%*s" : "%-*s", width, bufstr);
+}
+
+/* Display the register whose target number is TNUM.  If TNUM is -1, print all
+   registers (all == 1) or some registers (all == 0).
+
+   To reduce the number of pages per register dump, use multi-column layout if
+   possible.  Layout rules:
+
+     - Registers are displayed from left -> right and then top -> bottom in
+       the internal register ordering.
+
+     - All registers with the same max value width are distributed across the
+       same number of columns, within which values are vertically aligned.  */
+
+void
+cliregs_info (int tnum, int all)
+{
+  int i, j, from, to, flags, size, multi, elen, count;
+  struct reg *reg;
+  struct inf *inf;
+  struct fmt *fmt;
+  char *name, *rawbuf;
+  struct type *type, *type2;
+
+  inf = gdbarch_data (inf_id);
+  rawbuf = (char *) alloca (MAX_REGISTER_RAW_SIZE);
+  multi = tnum == -1;
+
+  /* Choose register range.  */
+  if (multi)
+    {
+      from = 0;
+      to = regs_count ();
+    }
+  else
+    {
+      from = regs_index (tnum);
+      to = from + 1;
+    }
+
+  /* Display registers.  */
+  for (i = from; i < to; i++)
+    {
+      reg = inf->regs + i;
+      flags = regs_inum_flags (i);
+      tnum = regs_inum_tnum (i);
+
+      if (multi)
+	{
+	  if (flags & REGS_HIDEALL)
+	    continue;
+	  if (!all && (flags & REGS_HIDESOME))
+	    continue;
+	}
+      fmt = &reg->fmt[multi ? (all ? ALL : SOME) :
+		      (flags & REGS_HIDESOME ? ALL : SOME)];
+
+      /* Display name.  */
+      printf_filtered ("%-*s$%s%" XSTRING (NAMEGAP) "s",
+		       fmt->namepad, "", regs_inum_name (i), "");
+
+      /* Don't display registers with side-effects unless explicitly
+	 requested. */
+      if (multi && (flags & REGS_REFFECT))
+	printf_filtered ("%-*s", reg->width1 + reg->width2, DISP_EFFECT);
+
+      /* Try to retrieve raw value.  */
+      else if (regs_fetch_frame (selected_frame, tnum, rawbuf) <= 0)
+	printf_filtered ("%-*s", reg->width1 + reg->width2, DISP_NOVAL);
+
+      /* Display the retrieved value.  */
+      else
+	{
+	  type = regs_inum_type (i);
+
+	  /* Convert to virtual.  */
+	  if (REGISTER_CONVERTIBLE (tnum))
+	    REGISTER_CONVERT_TO_VIRTUAL (tnum, type, rawbuf, inf->virtbuf);
+	  else
+	    memcpy (inf->virtbuf, rawbuf, TYPE_LENGTH (type));
+
+	  /* Print vectors in hex.  */
+	  if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
+	    {
+	      type2 = TYPE_TARGET_TYPE (type);
+	      elen = TYPE_LENGTH (type2);
+	      count = TYPE_LENGTH (type) / elen;
+
+	      for (j = 0; j < count; j++)
+		printval (type2, inf->virtbuf + j * elen, 'x', 0,
+			  reg->width1 + (j ? VECGAP : 0));
+	    }
+
+	  /* Print non-floats in hex and decimal.  FIXME: should pointers be
+	     printed only in hex?  */
+	  else if (TYPE_CODE (type) != TYPE_CODE_FLT)
+	    {
+	      printval (type, inf->virtbuf, 'x', 1, reg->width1);
+	      printval (type, inf->virtbuf, 0, 1, reg->width2);
+	    }
+
+	  /* Print floats as numbers and hex strings.  */
+	  else
+	    {
+#ifdef INVALID_FLOAT
+	      if (INVALID_FLOAT (inf->virtbuf, TYPE_LENGTH (type)))
+		printf_filtered ("%-*s", reg->width1, DISP_INVFLOAT);
+	      else
+#endif
+		printval (type, inf->virtbuf, 0, 0, reg->width1);
+
+	      puts_filtered ("(raw 0x");
+	      for (j = 0, size = regs_inum_size (i); j < size; j++)
+		{
+		  int idx = TARGET_BYTE_ORDER == BIG_ENDIAN ? j
+		    : size - 1 - j;
+		  printf_filtered ("%02x", (unsigned char) rawbuf[idx]);
+		}
+	      puts_filtered (")");
+	    }
+	}
+
+      /* Print column gap or newline.  */
+      if (fmt->newline || !multi)
+	putchar_filtered ('\n');
+      else
+	printf_filtered ("%" XSTRING (COLGAP) "s", "");
+    }
+}
+
+/* Store in WIDTH1P and WIDTH2P the widths to use for the two values displayed
+   for a register with TYPE and SIZE, and return the sum of those two widths.
+
+   FIXME: this should call methods in struct language_defn.
+
+   FIXME: need to handle line wrapping within values, e.g. vector registers.
+   Value gaps should be skipped at the beginning of wrapped lines, so they'll
+   have to be stored separately rather than in *WIDTH2P.  Vector registers may
+   need multiple wraps, so each register probably will need a format array,
+   each element describing one value.  */
+
+static int
+init_val_widths (struct type *type, int size, int *width1p, int *width2p)
+{
+  int len, slen, elen, count, hex, dec, max, i;
+  const char *unavail[] = { DISP_NOVAL, DISP_EFFECT, NULL };
+
+  len = TYPE_LENGTH (type);
+
+  /* Vectors get printed as a space-separated hexadecimal list.  */
+  if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
+    {
+      elen = TYPE_LENGTH (TYPE_TARGET_TYPE (type));
+      len = len / elen;
+      *width1p = (2 + elen * 2) * len + VECGAP * (len - 1);
+      *width2p = 0;
+    }
+
+  /* Floats get printed as floats followed by "(raw <hex>)".  */
+  else if (TYPE_CODE (type) == TYPE_CODE_FLT)
+    {
+      /* FIXME: print_floating() gives floats more digits than they need, so
+         use its hard-coded values rather than inferring from float width.  */
+
+      if (len < sizeof (double))
+	*width1p = 11;
+      else if (len == sizeof (double))
+	*width1p = 19;
+#ifdef PRINTF_HAS_LONG_DOUBLE
+      else
+	*width1p = 37;
+#else
+      else
+	*width1p = 19;
+#endif
+      *width1p += VALGAP;	/* spaces between float and hex */
+
+      slen = sizeof (DISP_INVFLOAT) + 1;
+      if (*width1p < slen)
+	*width1p = slen;
+
+      *width2p = (8 +		/* (raw 0x) */
+		  size * 2);	/* 2 hex digits per byte */
+    }
+
+  /* Everything else gets printed in hex followed by signed decimal.  */
+  else
+    {
+      hex = (2 +		/* 0x */
+	     len * 2);		/* 2 hex digits per byte */
+
+      dec = (VALGAP +		/* spaces between hex and decimal */
+	     1);		/* sign */
+      if (len == 1)
+	dec += 3;
+      else
+	dec += len * 5 / 2;	/* poor man's base-10 logarithm */
+
+      *width1p = hex;
+      *width2p = dec;
+    }
+
+  /* Unavailable-value strings.  */
+  max = *width1p + *width2p;
+  for (i = 0; unavail[i]; i++)
+    if (max < (len = strlen (unavail[i])))
+      max = len;
+
+  return max;
+}
+
+/* Calculate column layout for each register in both "info registers" and
+   "info all-registers" output.  Algorithm:
+
+   (1) Divide the registers into subsets where all members of a subset
+       have the same max value width.
+
+   (2) Find the maximum name length in each subset.
+
+   (3) Pad register names to the maximum lengths found in (2).
+
+   (4) Calculate the maximum number of columns across which registers in
+       each subset can be distributed, and distribute them accordingly.  */
+
+static void
+init_columns (struct inf *inf)
+{
+  int i, j, width, w, *widths, *namemax, namelen, *ncols, ncols_prev, col;
+  int width1, width2, maxwidth, nregs, flags, size;
+  struct reg *reg, *reg_prev;
+  struct type *type;
+  char *name;
+
+  nregs = regs_count ();
+
+  /* Find the maximum width.  */
+  for (i = maxwidth = 0; i < nregs; i++)
+    {
+      size = regs_inum_size (i);
+      type = regs_inum_type (i);
+
+      width = init_val_widths (type, size, &width1, &width2);
+      if (width > maxwidth)
+	maxwidth = width;
+    }
+
+  /* Avoid "might be used uninitialized" warning.  */
+  reg_prev = NULL;
+
+  /* Max widths versus max name lengths, for implementing both (1) and (2).  */
+  namemax = (int *) xcalloc (maxwidth + 1, sizeof (int));
+
+  /* Max widths versus column counts, for implementing (4).  */
+  ncols = (int *) xcalloc (maxwidth + 1, sizeof (int));
+
+  /* Register indices versus max widths, for caching init_val_widths().  */
+  widths = (int *) xmalloc (nregs * sizeof (int));
+
+  for (i = SOME; i <= ALL; i++)
+    {
+      /* (1) and (2): create subsets, calculate max name.  */
+      for (j = 0; j < nregs; j++)
+	{
+	  reg = inf->regs + j;
+	  flags = regs_inum_flags (j);
+
+	  if (flags & REGS_HIDEALL)
+	    continue;
+
+	  if (i == ALL)
+	    width = widths[j];
+	  else
+	    {
+	      size = regs_inum_size (j);
+	      type = regs_inum_type (j);
+	      widths[j] = width = init_val_widths (type, size, &reg->width1,
+						   &reg->width2);
+	      if (flags & REGS_HIDESOME)
+		continue;
+	    }
+
+	  namelen = strlen (regs_inum_name (j));
+	  if (namelen > namemax[width])
+	    namemax[width] = namelen;
+	}
+
+      /* (3) and (4): pad register names, calculate columns, distribute.  */
+      ncols_prev = col = 0;
+      for (j = 0; j < nregs; j++)
+	{
+	  reg = inf->regs + j;
+	  flags = regs_inum_flags (j);
+
+	  if (flags & REGS_HIDEALL)
+	    continue;
+	  if (i == SOME && flags & REGS_HIDESOME)
+	    continue;
+
+	  /* Pad register name.  */
+	  name = regs_inum_name (j);
+	  width = widths[j];
+	  reg->fmt[i].namepad = namemax[width] - strlen (name);
+
+	  /* Calculate columns.  */
+	  if (!ncols[width])
+	    {
+	      w = (1			/* leading $ */
+		   + namemax[width]	/* name + padding */
+		   + NAMEGAP		/* space before value */
+		   + width);		/* value */
+	      ncols[width] = (SCREENW + COLGAP) / (w + COLGAP);
+	      if (!ncols[width])
+		ncols[width] = 1;
+	    }
+
+	  /* Distribute.  */
+	  if (ncols_prev != ncols[width] || col++ == ncols_prev)
+	    col = 1;
+	  else
+	    reg_prev->fmt[i].newline = 0;
+
+	  reg->fmt[i].newline = 1;
+	  ncols_prev = ncols[width];
+	  reg_prev = reg;
+	}
+    }
+}
+
+/* Create and return initialization information for displaying the current
+   architecture's registers.  */
+
+static void *
+cliregs_init (void)
+{
+  struct inf *inf;
+
+  inf = (struct inf *) xmalloc (sizeof (struct inf));
+  inf->regs = (struct reg *) xmalloc (regs_count () * sizeof (struct reg));
+  inf->virtbuf = (char *) xmalloc (MAX_REGISTER_VIRTUAL_SIZE);
+  init_columns (inf);
+
+  return inf;
+}
+
+/* Module initialization.  */
+
+void
+_initialize_cliregs (void)
+{
+  inf_id = register_gdbarch_data (cliregs_init);
+}
Index: gdb/cli/cli-regs.h
===================================================================
diff -up /dev/null gdb/cli/cli-regs.h
--- /dev/null	Sun Feb 12 03:29:56 1995
+++ gdb/cli/cli-regs.h	Wed Jan  3 15:08:48 2001
@@ -0,0 +1,26 @@
+/* Header file for GDB CLI register display library.
+   Copyright (C) 2000 Free Software Foundation, Inc.
+
+   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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#if !defined (CLI_DECODE_H)
+# define CLI_DECODE_H 1
+
+/* DO_REGISTERS_INFO callback.  */
+
+extern void cliregs_info (int tnum, int all);
+
+#endif
Index: gdb/gdbarch.c
===================================================================
diff -up gdb/gdbarch.c gdb/gdbarch.c
--- gdb/gdbarch.c	Wed Jan  3 15:37:28 2001
+++ gdb/gdbarch.c	Wed Jan  3 15:36:22 2001
@@ -168,6 +168,7 @@ struct gdbarch
   int max_register_virtual_size;
   gdbarch_register_virtual_type_ftype *register_virtual_type;
   gdbarch_do_registers_info_ftype *do_registers_info;
+  void * register_list;
   gdbarch_register_sim_regno_ftype *register_sim_regno;
   gdbarch_register_bytes_ok_ftype *register_bytes_ok;
   int use_generic_dummy_frames;
@@ -316,6 +317,7 @@ struct gdbarch startup_gdbarch =
   0,
   0,
   0,
+  0,
   generic_get_saved_register,
   0,
   0,
@@ -940,6 +942,11 @@ gdbarch_dump (struct gdbarch *gdbarch, s
                       "DO_REGISTERS_INFO(reg_nr, fpregs)",
                       XSTRING (DO_REGISTERS_INFO (reg_nr, fpregs)));
 #endif
+#ifdef REGISTER_LIST
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: REGISTER_LIST # %s\n",
+                      XSTRING (REGISTER_LIST));
+#endif
 #ifdef REGISTER_SIM_REGNO
   fprintf_unfiltered (file,
                       "gdbarch_dump: %s # %s\n",
@@ -1607,6 +1614,11 @@ gdbarch_dump (struct gdbarch *gdbarch, s
                         (long) current_gdbarch->do_registers_info
                         /*DO_REGISTERS_INFO ()*/);
 #endif
+#ifdef REGISTER_LIST
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: REGISTER_LIST = %ld\n",
+                      (long) REGISTER_LIST);
+#endif
 #ifdef REGISTER_SIM_REGNO
   if (GDB_MULTI_ARCH)
     fprintf_unfiltered (file,
@@ -2760,6 +2772,21 @@ set_gdbarch_do_registers_info (struct gd
                                gdbarch_do_registers_info_ftype do_registers_info)
 {
   gdbarch->do_registers_info = do_registers_info;
+}
+
+void *
+gdbarch_register_list (struct gdbarch *gdbarch)
+{
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_register_list called\n");
+  return gdbarch->register_list;
+}
+
+void
+set_gdbarch_register_list (struct gdbarch *gdbarch,
+                           void * register_list)
+{
+  gdbarch->register_list = register_list;
 }
 
 int
Index: gdb/gdbarch.h
===================================================================
diff -up gdb/gdbarch.h gdb/gdbarch.h
--- gdb/gdbarch.h	Wed Jan  3 15:37:37 2001
+++ gdb/gdbarch.h	Wed Jan  3 15:36:05 2001
@@ -604,6 +604,17 @@ extern void set_gdbarch_do_registers_inf
 #endif
 #endif
 
+/* If non-null, register definition list provided by the target.  See regs.h
+   for details. */
+
+extern void * gdbarch_register_list (struct gdbarch *gdbarch);
+extern void set_gdbarch_register_list (struct gdbarch *gdbarch, void * register_list);
+#if GDB_MULTI_ARCH
+#if (GDB_MULTI_ARCH > GDB_MULTI_ARCH_PARTIAL) || !defined (REGISTER_LIST)
+#define REGISTER_LIST (gdbarch_register_list (current_gdbarch))
+#endif
+#endif
+
 /* MAP a GDB RAW register number onto a simulator register number.  See
    also include/...-sim.h. */
 
Index: gdb/gdbarch.sh
===================================================================
diff -up gdb/gdbarch.sh gdb/gdbarch.sh
--- gdb/gdbarch.sh	Wed Jan  3 15:37:53 2001
+++ gdb/gdbarch.sh	Wed Jan  3 15:35:37 2001
@@ -392,6 +392,9 @@ f:2:REGISTER_VIRTUAL_SIZE:int:register_v
 v:2:MAX_REGISTER_VIRTUAL_SIZE:int:max_register_virtual_size::::0:-1
 f:2:REGISTER_VIRTUAL_TYPE:struct type *:register_virtual_type:int reg_nr:reg_nr::0:0
 f:2:DO_REGISTERS_INFO:void:do_registers_info:int reg_nr, int fpregs:reg_nr, fpregs:::do_registers_info::0
+# If non-null, register definition list provided by the target.  See regs.h
+# for details.
+v:2:REGISTER_LIST:void *:register_list
 # MAP a GDB RAW register number onto a simulator register number.  See
 # also include/...-sim.h.
 f:2:REGISTER_SIM_REGNO:int:register_sim_regno:int reg_nr:reg_nr:::default_register_sim_regno::0
Index: gdb/parse.c
===================================================================
diff -up gdb/parse.c gdb/parse.c
--- gdb/parse.c	Wed Jan  3 15:38:02 2001
+++ gdb/parse.c	Tue Jan  2 22:49:48 2001
@@ -46,6 +46,7 @@
 #include "symfile.h"		/* for overlay functions */
 #include "inferior.h"		/* for NUM_PSEUDO_REGS.  NOTE: replace 
 				   with "gdbarch.h" when appropriate.  */
+#include "regs.h"
 
 
 /* Symbols which architectures can redefine.  */
@@ -115,6 +116,9 @@ int
 target_map_name_to_register (char *str, int len)
 {
   int i;
+
+  if (REGISTER_LIST)
+    return regs_name_tnum (str, len);
 
   /* First try target specific aliases. We try these first because on some 
      systems standard names can be context dependent (eg. $pc on a 
Index: gdb/regcache.c
===================================================================
diff -up gdb/regcache.c gdb/regcache.c
--- gdb/regcache.c	Wed Jan  3 15:38:10 2001
+++ gdb/regcache.c	Tue Jan  2 22:49:48 2001
@@ -25,6 +25,7 @@
 #include "target.h"
 #include "gdbarch.h"
 #include "gdbcmd.h"
+#include "regs.h"
 
 /*
  * DATA STRUCTURE
@@ -66,16 +67,26 @@ static int registers_pid = -1;
 int
 register_cached (int regnum)
 {
-  return register_valid[regnum];
+  if (REGISTER_LIST)
+    return regs_valid (regnum);
+  else
+    return register_valid[regnum];
 }
 
 /* Record that REGNUM's value is cached if STATE is >0, uncached but
-   fetchable if STATE is 0, and uncached and unfetchable if STATE is <0.  */
+   fetchable if STATE is 0, and uncached and unfetchable if STATE is <0.
+
+   regs.c's caching mechanism requires that this function be called whenever a
+   register's cached state changes, including when updating a valid value with
+   a new valid value.  */
 
 void
 set_register_cached (int regnum, int state)
 {
-  register_valid[regnum] = state;
+  if (REGISTER_LIST)
+    regs_set_valid (regnum, state);
+  else
+    register_valid[regnum] = state;
 }
 
 /* REGISTER_CHANGED
@@ -93,7 +104,9 @@ register_changed (int regnum)
 char *
 register_buffer (int regnum)
 {
-  if (regnum < 0)
+  if (REGISTER_LIST)
+    return regs_buffer (regnum);
+  else if (regnum < 0)
     return registers;
   else
     return &registers[REGISTER_BYTE (regnum)];
@@ -104,7 +117,10 @@ register_buffer (int regnum)
 static int
 real_register (int regnum)
 {
-  return regnum >= 0 && regnum < NUM_REGS;
+  if (REGISTER_LIST)
+    return regs_real (regnum);
+  else
+    return regnum >= 0 && regnum < NUM_REGS;
 }
 
 /* Return whether register REGNUM is a pseudo register.  */
@@ -112,7 +128,10 @@ real_register (int regnum)
 static int
 pseudo_register (int regnum)
 {
-  return regnum >= NUM_REGS && regnum < NUM_REGS + NUM_PSEUDO_REGS;
+  if (REGISTER_LIST)
+    return regs_pseudo (regnum);
+  else
+    return regnum >= NUM_REGS && regnum < NUM_REGS + NUM_PSEUDO_REGS;
 }
 
 /* Fetch register REGNUM into the cache.  */
@@ -928,10 +947,13 @@ build_regcache (void)
   int sizeof_registers = REGISTER_BYTES + /* SLOP */ 256;
   int sizeof_register_valid = 
     (NUM_REGS + NUM_PSEUDO_REGS) * sizeof (*register_valid);
-  registers = xmalloc (sizeof_registers);
-  memset (registers, 0, sizeof_registers);
-  register_valid = xmalloc (sizeof_register_valid);
-  memset (register_valid, 0, sizeof_register_valid);
+  if (!REGISTER_LIST)
+    {
+      registers = xmalloc (sizeof_registers);
+      memset (registers, 0, sizeof_registers);
+      register_valid = xmalloc (sizeof_register_valid);
+      memset (register_valid, 0, sizeof_register_valid);
+    }
 }
 
 void
Index: gdb/regs.c
===================================================================
diff -up /dev/null gdb/regs.c
--- /dev/null	Sun Feb 12 03:29:56 1995
+++ gdb/regs.c	Wed Jan  3 15:06:48 2001
@@ -0,0 +1,1662 @@
+/* Target register definition interface for GDB, the GNU debugger.
+   Copyright 2000 Free Software Foundation, Inc.
+
+   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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <stdlib.h>
+
+#include "defs.h"
+#include "inferior.h"
+#include "arch-utils.h"
+#include "gdbcore.h"
+#include "symfile.h"
+#include "regs.h"
+#include "cli/cli-regs.h"
+
+/* Register flag bits used internally by this module.  */
+
+enum
+  {
+    REGS_MAPPED = 0x80000000,	/* register is memory-mapped */
+    REGS_PSEUDO = 0x40000000,	/* pseudo register */
+    REGS_REAL = 0x20000000	/* real register */
+  };
+
+/* Register value type.  */
+
+enum valtype
+  {
+    val_nofetch,		/* not fetchable */
+    val_fetch,			/* fetchable (innermost frame if real
+				   register, any frame if pseudo register) */
+    val_real,			/* real register value stored in
+				   regval.rawbuf (innermost frame) */
+    val_pseudo,			/* pseudo register value stored in
+				   regval.rawbuf (any frame) */
+    val_real2,			/* reference to real register (non-innermost
+				   frame) */
+    val_memory,			/* fetchable from memory (non-innermost
+				   frame) */
+    val_calc			/* calculated value stored in regval.rawbuf
+				   (non-innermost frame) */
+  };
+
+/* Register value information.  */
+
+struct regval
+  {
+    enum valtype type;		/* value type */
+    CORE_ADDR addr;		/* memory address if .type is val_memory */
+    char *rawbuf;		/* value if .type is val_calc, val_real, or
+				   val_pseudo */
+  };
+
+/* Per-register information.  */
+
+struct reg
+  {
+    int tnum;			/* target register number (must never
+				   change) */
+    char *name;			/* register name, may be null */
+    int dnum;			/* debug info register number */
+    int size;			/* raw size, in bytes */
+    struct type **type;		/* virtual data type */
+    CORE_ADDR mem;		/* if .flags & REGS_MAPPED, register's memory
+				   location */
+    unsigned int flags;		/* REGS_* bitmask */
+    regs_rpseudo_ftype rpseudo;	/* if non-null, pseudo register read
+				   callback */
+    regs_wpseudo_ftype wpseudo;	/* if non-null, pseudo register write
+				   callback */
+    void *data;			/* opaque data passed to .rpseudo and
+				   .wpseudo */
+    int *parents;		/* if non-null, -1-terminated list of
+				   registers on which this register's value
+				   depends */
+    struct reg **children;	/* if non-null, null-terminated list of
+				   registers whose values depend on this
+				   register's */
+    int offset;			/* byte offset in raw register buffer */
+    struct regval val;		/* information about this register's value */
+  };
+
+/* Per-architecture information for internal use by this module.  */
+
+struct inf
+  {
+    int nregs;			/* total number of registers */
+    int nnregs;			/* number of named registers */
+    int nmregs;			/* number of memory-mapped registers */
+    struct reg *regs;		/* per-register information */
+    struct reg **tnums;		/* .regs pointers sorted by number */
+    struct reg **names;		/* .regs pointers sorted by name */
+    struct reg **mems;		/* .regs pointers sorted by memory location */
+    char *cache;		/* storage for all raw register values */
+    int cachesize;		/* size of .cache */
+    int rawmax;			/* largest possible raw register size */
+    char *namebuf;		/* for storing any register name */
+    int namemax;		/* longest string that fits in .namebuf */
+    void (*caller_regs)		/* callback for locating caller's regs */
+      (struct frame_info *);
+  };
+
+/* Initialization context returned by regs_init_start().  */
+
+struct regs_init_context
+  {
+    struct gdbarch *gdbarch;
+    struct inf *inf;
+    int virtmax;
+    int maxtnum;
+    int regssz;
+  };
+
+/* Memory region search key used by find_mem().  */
+
+struct memfind
+  {
+    CORE_ADDR addr;		/* start of memory region */
+    int len;			/* length of region */
+  };
+
+/* Back end to find_*(): return the <register> in LEN-element ARRAY for
+   which CMP (KEY, <register>) returns 0, or null if no such register exists.  */
+
+static struct reg *
+find (const void *key, struct reg **array, int len,
+      int (*cmp)(const void *, const void *))
+{
+  struct reg **found;
+
+  found = bsearch (key, array, len, sizeof (struct reg *), cmp);
+  return found ? *found : NULL;
+}
+
+/* bsearch() comparison function: return -1, 0, or 1 according to whether
+   register number *TNUMP is less than, equal to, or greater than register
+   *REGP's number.  */
+
+static int
+find_tnum_cmp (const void *tnump, const void *regp)
+{
+  int tnum;
+  struct reg *reg;
+
+  tnum = *(int *) tnump;
+  reg = *(struct reg **) regp;
+  return tnum < reg->tnum ? -1 : tnum > reg->tnum;
+}
+
+/* bsearch() comparison function: return -1, 0, or 1 according to whether
+   register NAME is lexically less than, equal to, or greater than register
+   *REGP's name.  */
+
+static int
+find_name_cmp (const void *name, const void *regp)
+{
+  struct reg *reg;
+
+  reg = *(struct reg **) regp;
+  return strcasecmp (name, reg->name);
+}
+
+/* bsearch() comparison function: return -1, 0, or 1 according to whether
+   memory region *MEMP precedes, overlaps, or follows register *REGP's memory
+   location.  */
+
+static int
+find_mem_cmp (const void *memp, const void *regp)
+{
+  struct memfind *mem;
+  struct reg *reg;
+
+  mem = (struct memfind *) memp;
+  reg = *(struct reg **) regp;
+  return mem->addr + mem->len <= reg->mem ? -1 :
+    mem->addr >= reg->mem + reg->size;
+}
+
+/* If a register has index number INUM, return that register.  Otherwise, if
+   !CALLER, return null, else throw an error message identifying CALLER.  */
+
+static struct reg *
+find_inum (int inum, char *caller)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  if (inum >= 0 && inum < inf->nregs)
+    return inf->regs + inum;
+  if (caller)
+    internal_error ("%s: invalid register index number %d", caller, inum);
+  return NULL;
+}
+
+/* If a register has target number TNUM, return that register.  Otherwise, if
+   !CALLER, return null, else throw an error message identifying CALLER.  */
+
+static struct reg *
+find_tnum (int tnum, char *caller)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find (&tnum, inf->tnums, inf->nregs, find_tnum_cmp);
+  if (reg)
+    return reg;
+  if (caller)
+    internal_error ("%s: invalid register target number %d", caller, tnum);
+  return NULL;
+}
+
+/* If a register overlaps the LEN-byte memory region starting at ADDR, return
+   a pointer to that register's entry in the memory lookup array, else return
+   null.  */
+
+static struct reg **
+find_mem (CORE_ADDR addr, int len)
+{
+  struct reg **found;
+  struct memfind mem;
+  struct inf *inf;
+
+  inf = REGISTER_LIST;
+  if (!inf->nmregs)
+    return NULL;
+
+  mem.addr = addr;
+  mem.len = len;
+  return bsearch (&mem, inf->mems, inf->nmregs, sizeof (struct reg *),
+		  find_mem_cmp);
+}
+
+/* Return the valtype corresponding to integer VALIDITY, treating VALID as
+   corresponding to 1.  */
+
+static enum valtype
+int_valtype (int validity, enum valtype valid)
+{
+  if (validity < 0)
+    return val_nofetch;
+  if (validity == 0)
+    return val_fetch;
+  return valid;
+}
+
+/* Set TNUM's cache state in the innermost frame to STATE.  */
+
+static void
+set_valid (struct reg *reg, int state)
+{
+  struct reg **child;
+  struct regval *val;
+
+  reg->val.type = int_valtype (state, reg->flags & REGS_PSEUDO ?
+				  val_pseudo : val_real);
+
+  /* Invalidate dependent registers.  */
+  if (reg->children)
+    for (child = reg->children; *child; child++)
+      (*child)->val.type = val_fetch;
+}
+
+/* Try to fetch pseudo register REG's value in FRAME into VAL.  */
+
+static void
+fetch_pseudo (struct frame_info *frame, struct reg *reg,
+	      struct regval *val)
+{
+  int valid;
+  char *rawbuf;
+
+  if (!reg->rpseudo)
+    return;
+
+  rawbuf = (char *) alloca (MAX_REGISTER_RAW_SIZE);
+  valid = reg->rpseudo (frame, reg->tnum, reg->parents, reg->data, rawbuf);
+
+  val->type = int_valtype (valid, val_pseudo);
+  memcpy (val->rawbuf, rawbuf, reg->size);
+}
+
+/* Try to store pseudo register REG's value in FRAME to VAL.  */
+
+static void
+store_pseudo (struct frame_info *frame, struct reg *reg,
+	      struct regval *val)
+{
+  int valid;
+  char *rawbuf;
+
+  if (!reg->wpseudo)
+    return;
+
+  rawbuf = (char *) alloca (MAX_REGISTER_RAW_SIZE);
+  memcpy (rawbuf, val->rawbuf, reg->size);
+
+  valid = reg->wpseudo (frame, reg->tnum, reg->parents, reg->data, rawbuf);
+  val->type = int_valtype (valid, val_pseudo);
+}
+
+/* Return FRAME's caller's register information, retrieving it first if
+   necessary.  */
+
+static struct regval *
+caller_vals (struct frame_info *frame)
+{
+  struct inf *inf;
+  struct regval *vals, *vals_next, *val, *val2;
+  char *rawbufs, *dummybuf;
+  struct reg *reg2;
+  int i;
+
+  /* Don't retrieve twice.  */
+  if (frame->extra_info)
+    vals = (struct regval *) frame->extra_info;
+
+  else
+    {
+      /* Allocate space for the frame's register information.  */
+      inf = REGISTER_LIST;
+      vals = frame_obstack_alloc (sizeof (struct regval) * inf->nregs);
+      frame->extra_info = (struct frame_extra_info *) vals;
+      rawbufs = frame_obstack_alloc (inf->cachesize);
+
+      if (frame->next)
+	vals_next = (struct regval *) frame->next->extra_info;
+      else
+	vals_next = NULL;
+
+      /* If FRAME is a generic dummy frame, copy its saved caller state.  */
+      if (!USE_GENERIC_DUMMY_FRAMES)
+	dummybuf = NULL;
+      else
+	{
+	  dummybuf = generic_find_dummy_frame (frame->pc, frame->frame);
+	  if (dummybuf)
+	    memcpy (rawbufs, dummybuf, inf->cachesize);
+	}
+
+      /* Initialize the caller's register state.  */
+      for (i = 0; i < inf->nregs; i++)
+	{
+	  val = vals + i;
+	  reg2 = inf->regs + i;
+
+	  val->rawbuf = rawbufs;
+	  rawbufs += reg2->size;
+
+	  /* Pseudo registers should be regenerated in each frame.  */
+	  if (reg2->flags & REGS_PSEUDO)
+	    val->type = val_fetch;
+
+	  /* Dummy frame caller's registers are saved by value.  */
+	  else if (dummybuf)
+	    val->type = val_calc;
+
+	  /* Initialize the caller's real register state to that of FRMAE's
+	     so that inf->caller_regs() can update it.  */
+
+	  /* Innermost frame's real registers are the current registers.  */
+	  else if (!vals_next)
+	    val->type = val_real2;
+
+	  /* Non-innermost frame's real registers are in vals_next.  */
+	  else
+	    {
+	      val2 = vals_next + i;
+	      val->type = val2->type;
+	      if (val->type == val_memory)
+		val->addr = val2->addr;
+	      else if (val->type == val_calc)
+		memcpy (val->rawbuf, val2->rawbuf, reg2->size);
+	    }
+	}
+
+      /* Target does the real work of converting FRAME's register state to
+         its caller's.  Maybe someday a generic emulator will do this.  */
+      if (!dummybuf)
+	inf->caller_regs (frame);
+    }
+
+  return vals;
+}
+
+/* Return register REG's value information in FRAME's caller, retrieving
+   FRAME's caller information first if necessary.  */
+
+static struct regval *
+caller_val (struct frame_info *frame, struct reg *reg)
+{
+  struct inf *inf;
+  struct regval *vals;
+
+  inf = REGISTER_LIST;
+  vals = caller_vals (frame);
+
+  return vals + (reg - inf->regs);
+}
+
+/* Return register REG's value information in FRAME.  */
+
+static struct regval *
+frame_val (struct frame_info *frame, struct reg *reg)
+{
+  if (!frame || !frame->next)
+    return &reg->val;
+  return caller_val (frame->next, reg);
+}
+
+/* Try to fetch register REG's value from FRAME into RAWBUF.  Return 1 on
+   success, 0 on failure if a future attempt might succeed, -1 otherwise.  */
+
+static int
+fetch_frame (struct frame_info *frame, struct reg *reg, char *rawbuf)
+{
+  struct regval *val;
+
+  /* Locate the register's value info.  */
+  val = frame_val (frame, reg);
+  if (val->type == val_real2)
+    val = frame_val (NULL, reg);
+
+  /* Update the value info.  */
+  if (val->type == val_fetch)
+    {
+      if (reg->flags & REGS_PSEUDO)
+	fetch_pseudo (frame, reg, val);
+      else
+	/* Only possible in the innermost frame.  */
+	target_fetch_registers (reg->tnum);
+    }
+
+  /* Copy the value to RAWBUF and return.  */
+  switch (val->type)
+    {
+    case val_nofetch:
+      return -1;
+    case val_fetch:
+      return 0;
+    case val_calc:
+    case val_pseudo:
+    case val_real:
+      memcpy (rawbuf, val->rawbuf, reg->size);
+      return 1;
+    case val_memory:
+      return !target_read_memory (val->addr, rawbuf, reg->size);
+    case val_real2:
+    default:
+      internal_error ("fetch_frame: impossible value type");
+      return -1;
+    }
+}
+
+/* Try to fetch register TNUM's value from FRAME into RAWBUF.  Return 1 on
+   success, 0 on failure if a future attempt might succeed, -1 otherwise.  */
+
+int
+regs_fetch_frame (struct frame_info *frame, int tnum, char *rawbuf)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, "regs_fetch_frame");
+  return fetch_frame (frame, reg, rawbuf);
+}
+
+/* Try to store RAWBUF as register REG's value in FRAME.  Return success.  */
+
+static int
+store_frame (struct frame_info *frame, struct reg *reg, char *rawbuf)
+{
+  struct regval *val;
+
+  /* Locate the register's value info.  */
+  val = frame_val (frame, reg);
+
+  /* Handle memory and unwritable cases.  */
+  switch (val->type)
+    {
+    case val_memory:
+      return !target_write_memory (val->addr, rawbuf, reg->size);
+    case val_calc:
+    case val_real2:
+    case val_nofetch:
+      return 0;
+    default:
+      break;
+    }
+
+  /* It's a real register in the innermost frame or a pseudo register in any
+     frame.  Attempt the store.  */
+  val->type = val_fetch;
+  memcpy (val->rawbuf, rawbuf, reg->size);
+
+  if (reg->flags & REGS_PSEUDO)
+    store_pseudo (frame, reg, val);
+  else
+    target_store_registers (reg->tnum);
+
+  return val->type == val_pseudo || val->type == val_real;
+}
+
+/* Try to store RAWBUF as register TNUM's value in FRAME.  Return
+   success.  */
+
+int
+regs_store_frame (struct frame_info *frame, int tnum, char *rawbuf)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, "regs_store_frame");
+  return store_frame (frame, reg, rawbuf);
+}
+
+/* Return the value of register TNUM in FRAME.  */
+
+ULONGEST
+regs_get_frame (struct frame_info *frame, int tnum)
+{
+  struct inf *inf;
+  struct reg *reg;
+  char *rawbuf;
+
+  inf = REGISTER_LIST;
+  reg = find_tnum (tnum, "get_frame");
+  rawbuf = (char *) alloca (inf->rawmax);
+
+  if (fetch_frame (frame, reg, rawbuf) <= 0)
+    return 0;
+  return extract_unsigned_integer (rawbuf, reg->size);
+}
+
+/* Return the value of register TNUM in FRAME's caller.  */
+
+ULONGEST
+regs_get_caller (struct frame_info *frame, int tnum)
+{
+  struct frame_info prev_frame;
+
+  prev_frame.next = frame;
+  return regs_get_frame (&prev_frame, tnum);
+}
+
+/* Record the fact that register TNUM's value in FRAME's caller is:
+     - VALUE if LVAL is not_lval
+     - in memory location VALUE if LVAL is lval_memory
+     - in register TNUM if LVAL is lval_register  */
+
+void
+regs_set_caller (struct frame_info *frame, int tnum,
+		 enum lval_type lval, ULONGEST value)
+{
+  struct reg *reg;
+  struct regval *val;
+
+  reg = find_tnum (tnum, "regs_set_caller");
+  val = caller_val (frame, reg);
+
+  switch (lval)
+    {
+    case lval_memory:
+      val->type = val_memory;
+      val->addr = value;
+      break;
+    case lval_register:
+      val->type = val_real2;
+      break;
+    case not_lval:
+      val->type = val_calc;
+      store_unsigned_integer (val->rawbuf, reg->size, value);
+      break;
+    default:
+      internal_error ("regs_set_caller: illegal lval");
+      break;
+    }
+}
+
+/* Copy register TNUM1's caller information in FRAME to register
+   TNUM2's.  */
+
+void
+regs_copy_caller (struct frame_info *frame, int tnum1, int tnum2)
+{
+  struct reg *reg1, *reg2;
+  struct regval *val1, *val2;
+
+  reg1 = find_tnum (tnum1, "regs_copy_caller");
+  reg2 = find_tnum (tnum2, "regs_copy_caller");
+
+  val1 = caller_val (frame, reg1);
+  val2 = caller_val (frame, reg2);
+
+  val2->type = val1->type;
+  switch (val1->type)
+    {
+    case val_memory:
+      val2->addr = val1->addr;
+      break;
+    case val_real2:
+      if (fetch_frame (frame, reg1, val2->rawbuf) <= 0)
+	val2->type = val_nofetch;
+      else
+	val2->type = val_calc;
+      break;
+    case val_calc:
+      memcpy (val2->rawbuf, val1->rawbuf, reg2->size);
+      break;
+    default:
+      break;
+    }
+}
+
+/* Pseudo-register callback to read an alias register.  */
+
+int
+regs_rpseudo_alias (struct frame_info *frame, int tnum, int *parents,
+		    void *data, char *rawbuf)
+{
+  return regs_fetch_frame (frame, *parents, rawbuf);
+}
+
+/* Pseudo-register callback to write an alias register.  */
+
+int
+regs_wpseudo_alias (struct frame_info *frame, int tnum, int *parents,
+		    void *data, char *rawbuf)
+{
+  return regs_store_frame (frame, *parents, rawbuf);
+}
+
+/* Pseudo-register callback to read a vector register.  */
+
+int
+regs_rpseudo_vec (struct frame_info *frame, int tnum, int *parents,
+		  void *data, char *rawbuf)
+{
+  int *parent, size;
+  struct reg *reg;
+
+  reg = find_tnum (tnum, "regs_rpseudo_vec");
+  size = TYPE_LENGTH (TYPE_TARGET_TYPE (*reg->type));
+
+  for (parent = parents; *parent >= 0; parent++)
+    {
+      if (regs_fetch_frame (frame, *parent, rawbuf) <= 0)
+	return 0;
+      rawbuf += size;
+    }
+  return 1;
+}
+
+/* Pseudo-register callback to write a vector register.  */
+
+int
+regs_wpseudo_vec (struct frame_info *frame, int tnum, int *parents,
+		  void *data, char *rawbuf)
+{
+  int *parent, size;
+  struct reg *reg;
+
+  reg = find_tnum (tnum, "regs_wpseudo_vec");
+  size = TYPE_LENGTH (TYPE_TARGET_TYPE (*reg->type));
+
+  for (parent = parents; *parent >= 0; parent++)
+    {
+      if (!regs_store_frame (frame, *parent, rawbuf))
+	return 0;
+      rawbuf += size;
+    }
+  return 1;
+}
+
+/* Pseudo-register callback to sum all parent registers.  */
+
+int
+regs_rpseudo_sum (struct frame_info *frame, int tnum, int *parents,
+		  void *data, char *rawbuf)
+{
+  struct inf *inf;
+  struct reg *reg;
+  int *parent;
+  LONGEST sum;
+
+  inf = REGISTER_LIST;
+
+  for (sum = 0, parent = parents; *parent >= 0; parent++)
+    {
+      reg = find_tnum (*parent, "regs_rpseudo_sum");
+      if (fetch_frame (frame, reg, rawbuf) <= 0)
+	return 0;
+      sum += extract_unsigned_integer (rawbuf, reg->size);
+    }
+
+  reg = find_tnum (tnum, "regs_rpseudo_sum");
+  store_unsigned_integer (rawbuf, reg->size, sum);
+  return 1;
+}
+
+/* Pseudo-register callback to store in the first parent register the result
+   of subtracting the second parent register from RAWBUF.  */
+
+int
+regs_wpseudo_sub (struct frame_info *frame, int tnum, int *parents,
+		  void *data, char *rawbuf)
+{
+  struct inf *inf;
+  struct reg *reg;
+  LONGEST diff;
+
+  inf = REGISTER_LIST;
+  reg = find_tnum (tnum, "regs_wpseudo_sub");
+  diff = extract_unsigned_integer (rawbuf, reg->size);
+
+  reg = find_tnum (parents[1], "regs_wpseudo_sub");
+  if (fetch_frame (frame, reg, rawbuf) <= 0)
+    return 0;
+  diff -= extract_unsigned_integer (rawbuf, reg->size);
+
+  reg = find_tnum (parents[0], "regs_wpseudo_sub");
+  store_unsigned_integer (rawbuf, reg->size, diff);
+  return store_frame (frame, reg, rawbuf);
+}
+
+/* Retrieve information about register TNUM's value in FRAME.  Specifically:
+
+     - Store the raw value in RAW_BUFFER.
+
+     - Set *INVALIDP to whether the value can't be retrieved.
+
+     - If the current value in the register is correct for FRAME, store the
+       register's cache offset in ADDRP and set *LVALP to lval_register.
+
+     - If the value is saved in memory, store the memory address in ADDRP and
+       set *LVALP to lval_memory.
+
+     - If the value is calculated or unavailable, set *LVALP to lval_noval.
+  
+  Any or all of RAW_BUFFER, OPTIMIZEDP, ADDRP, and LVALP may be null.  */
+
+static void
+regs_get_saved_register (char *rawbuf, int *invalidp, CORE_ADDR *addrp,
+			 struct frame_info *frame, int tnum,
+			 enum lval_type *lvalp)
+{
+  struct reg *reg;
+  struct regval *val;
+  int invalid;
+  CORE_ADDR addr;
+  enum lval_type lval;
+
+  if (!target_has_registers)
+    error ("No registers.");
+
+  reg = find_tnum (tnum, "regs_get_saved_register");
+  val = frame_val (frame, reg);
+
+  /* Fetch the register's value if requested.  */
+  if (rawbuf)
+    invalid = fetch_frame (frame, reg, rawbuf) <= 0;
+  else
+    invalid = val->type == val_nofetch;
+
+  /* Infer other requested information.  */
+  switch (val->type)
+    {
+    case val_memory:
+      lval = lval_memory;
+      addr = val->addr;
+      break;
+    case val_real:
+      lval = lval_register;
+      addr = reg->offset;
+      break;
+    default:
+      lval = not_lval;
+      addr = 0;
+      break;
+    }
+
+  /* Return other requested information.  */
+  if (invalidp)
+    *invalidp = invalid;
+  if (addrp)
+    *addrp = addr;
+  if (lvalp)
+    *lvalp = lval;
+}
+
+/* Initialize newly-created FRAME's register information.  */
+
+static void
+regs_init_extra_frame_info (int fromleaf, struct frame_info *frame)
+{
+  frame->extra_info = NULL;
+}
+
+/* Initialize FRAME->saved_regs.
+
+   This is only called by (a) generic get_saved_register callbacks, which this
+   module replaces, and (b) frame_info(), which can handle a null
+   FRAME->saved_regs.
+
+   FIXME: either change frame_info() to understand this module's saved
+   register layout or else instantiate FRAME->saved_regs here.  */
+
+static void
+regs_frame_init_saved_regs (struct frame_info *frame)
+{
+  return;
+}
+
+/* Pop the topmost frame from the stack, restoring all saved registers.  */
+
+static void
+regs_pop_frame (void)
+{
+  struct frame_info *frame, prev_frame;
+  struct inf *inf;
+  struct reg *reg;
+  struct regval *vals, *val;
+  char *rawbuf;
+  int i;
+
+  frame = get_current_frame ();
+
+  if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
+    generic_pop_dummy_frame ();
+  else
+    {
+      inf = REGISTER_LIST;
+      vals = caller_vals (frame);
+      prev_frame.next = frame;
+      rawbuf = (char *) alloca (inf->rawmax);
+
+      /* Copy the caller's registers to the register cache.  */
+      for (i = 0; i < inf->nregs; i++)
+	{
+	  val = vals + i;
+	  if (val->type != val_calc && val->type != val_memory)
+	    continue;
+
+	  reg = inf->regs + i;
+	  if (fetch_frame (&prev_frame, reg, rawbuf) > 0)
+	    write_register_gen (reg->tnum, rawbuf);
+	}
+    }
+  flush_cached_frames ();
+}
+
+/* Return the PC saved in FRAME.  */
+
+static CORE_ADDR
+regs_frame_saved_pc (struct frame_info *frame)
+{
+  return regs_get_caller (frame, PC_REGNUM);
+}
+
+/* Return the FP saved in FRAME.  */
+
+static CORE_ADDR
+regs_frame_chain (struct frame_info *frame)
+{
+  return regs_get_caller (frame, SP_REGNUM);
+}
+
+/* Attempt to synchronize pseudo register TNUM's cached value with the
+   innermost frame's register state.  */
+
+static void
+regs_fetch_pseudo_register (int tnum)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find_tnum (tnum, "regs_fetch_pseudo_register");
+
+  if (reg->val.type == val_fetch)
+    fetch_pseudo (NULL, reg, &reg->val);
+}
+
+/* Attempt to synchronize the innermost frame's register state with
+   pseudo register TNUM's cached value.  */
+
+static void
+regs_store_pseudo_register (int tnum)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find_tnum (tnum, "regs_store_pseudo_register");
+
+  store_pseudo (NULL, reg, &reg->val);
+}
+
+/* Return the name of register number TNUM, or null if no such register
+   exists in the current architecture.  */
+
+static char *
+regs_register_name (int tnum)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, NULL);
+  if (!reg)
+    return NULL;
+  if (!reg->name)
+    return "";
+  return reg->name;
+}
+
+/* Return the offset within the raw register buffer of the first byte of space
+   for register TNUM.  */
+
+static int
+regs_register_byte (int tnum)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, "regs_register_byte");
+  return reg->offset;
+}
+
+/* Return the number of bytes of storage allocated in the raw register buffer
+   for register TNUM if that register is available, else return 0.  */
+
+static int
+regs_register_raw_size (int tnum)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, NULL);
+  if (!reg)
+    return 0;
+  return reg->size;
+}
+
+/* Return the type used for GDB's virtual representation of register TNUM.  */
+
+static struct type *
+regs_register_virtual_type (int tnum)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, "regs_register_virtual_type");
+  return *reg->type;
+}
+
+/* Return the number of bytes of storage needed for GDB's virtual
+   representation of register TNUM.  */
+
+static int
+regs_register_virtual_size (int tnum)
+{
+  return TYPE_LENGTH (regs_register_virtual_type (tnum));
+}
+
+/* Return the target number of register INUM.  */
+
+int
+regs_inum_tnum (int inum)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find_inum (inum, "regs_tnum");
+  return reg->tnum;
+}
+
+/* Return the name of register INUM.  */
+
+char *
+regs_inum_name (int inum)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find_inum (inum, "regs_name");
+  return reg->name;
+}
+
+/* Return the type of register INUM.  */
+
+struct type *
+regs_inum_type (int inum)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find_inum (inum, "regs_type");
+  return *reg->type;
+}
+
+/* Return the size of register INUM.  */
+
+int
+regs_inum_size (int inum)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find_inum (inum, "regs_size");
+  return reg->size;
+}
+
+/* Return the flags of register INUM.  */
+
+unsigned int
+regs_inum_flags (int inum)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find_inum (inum, "regs_flags");
+  return reg->flags;
+}
+
+/* Invalidate any memory-mapped registers that the LEN-byte target memory
+   region starting at ADDR overlaps.  This function should be called whenever
+   target memory is written.  */
+
+void
+regs_memwrite (CORE_ADDR addr, int len)
+{
+  struct reg *reg, **found;
+  struct memfind mem;
+  struct inf *inf;
+
+  inf = REGISTER_LIST;
+  if (!inf)
+    return;
+
+  found = find_mem (addr, len);
+  if (!found)
+    return;
+
+  /* The region may overlap more than one register.  Find the overlapped
+     register nearest the start of the region, then invalidate registers
+     upward from there.  */
+
+  while (found > inf->mems)
+    {
+      reg = found[-1];
+      if (reg->mem + reg->size <= addr)
+	break;
+      found--;
+    }
+
+  while (found < inf->mems + inf->nmregs)
+    {
+      reg = *found;
+      if (reg->mem >= addr + len)
+	break;
+      set_valid (reg, 0);
+    }
+}
+
+/* Return the target number of the register with LEN-byte NAME, or -1 if no
+   such register exists.  */
+
+int
+regs_name_tnum (char *name, int len)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  if (len > inf->namemax)
+    return -1;
+
+  memcpy (inf->namebuf, name, len);
+  inf->namebuf[len] = '\0';
+  reg = find (inf->namebuf, inf->names, inf->nnregs, find_name_cmp);
+  return reg ? reg->tnum : -1;
+}
+
+/* Return the target number of the register mapped to memory ADDR, or -1 if no
+   such register exists.  */
+
+int
+regs_addr_tnum (CORE_ADDR addr)
+{
+  struct reg *reg, **found;
+  struct memfind mem;
+  struct inf *inf;
+
+  found = find_mem (addr, 1);
+  if (!found)
+    return -1;
+
+  reg = *found;
+  if (reg->mem != addr)
+    return -1;
+
+  return reg->tnum;
+}
+
+/* Return the internal number of register TNUM.  */
+
+int
+regs_index (int tnum)
+{
+  struct inf *inf;
+  struct reg *reg;
+
+  inf = REGISTER_LIST;
+  reg = find_tnum (tnum, "regs_index");
+  return reg - inf->regs;
+}
+
+/* Return >0 if register TNUM's value in the innermost frame is cached, 0 if
+   it's uncached but fetchable, and <0 if it's uncached and unfetchable.  */
+
+int
+regs_valid (int tnum)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, "regs_valid");
+  switch (reg->val.type)
+    {
+    case val_nofetch:
+      return -1;
+    case val_fetch:
+      return 0;
+    default:
+      if (reg->flags & REGS_RTHRU)
+	return 0;
+      return 1;
+    }
+}
+
+/* Record that TNUM's value is cached if STATE is >0, uncached but
+   fetchable if STATE is 0, and uncached and unfetchable if STATE is <0.
+
+   This function must be called whenever a register's cached state changes,
+   including when updating a valid value with a new valid value.  */
+
+void
+regs_set_valid (int tnum, int state)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, "regs_set_valid");
+
+  /* Ignore pseudo registers, because remote.c incorrectly calls
+     supply_register() on all registers instead of just the ones fetched.  */
+  if (reg->flags & REGS_REAL)
+    set_valid (reg, state);
+}
+
+/* If TNUM >= 0, return a pointer to register TNUM's cache buffer area,
+   else return a pointer to the start of the cache buffer.  */
+
+char *
+regs_buffer (int tnum)
+{
+  struct reg *reg;
+  struct inf *inf;
+
+  inf = REGISTER_LIST;
+  if (tnum < 0)
+    return inf->cache;
+  else
+    {
+      reg = find_tnum (tnum, "regs_buffer");
+      return reg->val.rawbuf;
+    }
+}
+
+/* Return the number of registers in the current architecture.  */
+
+int
+regs_count (void)
+{
+  struct inf *inf;
+
+  inf = REGISTER_LIST;
+  return inf->nregs;
+}
+
+/* Return whether register TNUM is a real (not generated and not pseudo)
+   register.  */
+
+int
+regs_real (int tnum)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, NULL);
+  return reg && reg->flags & REGS_REAL;
+}
+
+/* Return whether register TNUM is a pseudo register.  */
+
+int
+regs_pseudo (int tnum)
+{
+  struct reg *reg;
+
+  reg = find_tnum (tnum, NULL);
+  return reg && reg->flags & REGS_PSEUDO;
+}
+
+/* qsort() comparison function: return -1, 0, or 1 according to whether the
+   name of register **REG1P is lexically less than, equal to, or greater than
+   the name of register **REG2P.  */
+
+static int
+init_name_cmp (const void *reg1p, const void *reg2p)
+{
+  struct reg *reg1, *reg2;
+
+  reg1 = *(struct reg **) reg1p;
+  reg2 = *(struct reg **) reg2p;
+  return strcasecmp (reg1->name, reg2->name);
+}
+
+/* qsort() comparison function: return -1, 0, or 1 according to whether the
+   number of register **REG1P is less than, equal to, or greater than the
+   number of register **REG2P.  */
+
+static int
+init_tnum_cmp (const void *reg1p, const void *reg2p)
+{
+  struct reg *reg1, *reg2;
+
+  reg1 = *(struct reg **) reg1p;
+  reg2 = *(struct reg **) reg2p;
+  return reg1->tnum < reg2->tnum ? -1 : reg1->tnum > reg2->tnum;
+}
+
+/* qsort() comparison function: return -1, 0, or 1 according to whether the
+   memory location of register **REG1P is less than, equal to, or greater than
+   the memory location of register **REG2P.  */
+
+static int
+init_mem_cmp (const void *reg1p, const void *reg2p)
+{
+  struct reg *reg1, *reg2;
+
+  reg1 = *(struct reg **) reg1p;
+  reg2 = *(struct reg **) reg2p;
+  return reg1->mem < reg2->mem ? -1 : reg1->mem > reg2->mem;
+}
+
+/* qsort() comparison function: return -1, 0, or 1 according to whether
+   integer *INT1P is less than, equal to, or greater than integer *INT2P.  */
+
+static int
+intcmp (const void *int1p, const void *int2p)
+{
+  int int1, int2;
+
+  int1 = *(int *) int1p;
+  int2 = *(int *) int2p;
+  return int1 < int2 ? -1 : int1 > int2;
+}
+
+/* Initialize INF's register lookup mechanisms.  */
+
+static void
+init_lookups (struct inf *inf)
+{
+  struct reg *reg;
+  int i, j, k;
+
+  inf->tnums = (struct reg **) xmalloc (sizeof (struct reg *) * inf->nregs);
+
+  if (inf->nnregs)
+    inf->names = (struct reg **) xmalloc (sizeof (struct reg *) * inf->nnregs);
+  else
+    inf->names = NULL;
+
+  if (inf->nmregs)
+    inf->mems = (struct reg **) xmalloc (sizeof (struct reg *) * inf->nmregs);
+  else
+    inf->mems = NULL;
+
+  for (i = j = k = 0; i < inf->nregs; i++)
+    {
+      reg = inf->regs + i;
+      inf->tnums[i] = reg;
+      if (reg->name)
+	inf->names[j++] = reg;
+      if (reg->flags & REGS_MAPPED)
+	inf->mems[k++] = reg;
+    }
+
+  qsort (inf->tnums, inf->nregs, sizeof (struct reg *), init_tnum_cmp);
+  qsort (inf->names, inf->nnregs, sizeof (struct reg *), init_name_cmp);
+  qsort (inf->mems, inf->nmregs, sizeof (struct reg *), init_mem_cmp);
+
+  inf->namebuf = (char *) xmalloc (inf->namemax + 1);
+}
+
+/* Initialize INF's cache storage.  */
+
+static void
+init_cache (struct inf *inf)
+{
+  int i, offset;
+  struct reg *reg;
+
+  inf->cache = (char *) xmalloc (inf->cachesize);
+  for (offset = i = 0; i < inf->nregs; i++)
+    {
+      reg = inf->tnums[i];
+      reg->offset = offset;
+      reg->val.type = val_fetch;
+      reg->val.rawbuf = inf->cache + offset;
+      offset += reg->size;
+    }
+}
+
+/* Initialize INF's pseudo register numbers.  */
+
+static void
+init_pseudo (struct inf *inf, struct regs_init_context *context)
+{
+  struct reg *reg;
+  int i;
+
+  for (i = 0; i < inf->nregs; i++)
+    {
+      reg = inf->regs + i;
+      if (reg->flags & REGS_PSEUDO)
+	reg->tnum = ++context->maxtnum;
+    }
+}
+
+/* Create register child lists in INF.  */
+
+static void
+init_children (struct inf *inf)
+{
+  struct reg *reg, *reg2;
+  int i, j, *pbuf, plen, *parent, nparents, *ccounts;
+
+  /* Allocate space for copying and sorting parent lists.  */
+  plen = 1;
+  pbuf = (int *) xmalloc (sizeof (int));
+
+  /* Allocate space for counting each register's children.  */
+  ccounts = (int *) xcalloc (inf->nregs, sizeof (int));
+
+  /* Add child lists to registers.  */
+  for (i = 0; i < inf->nregs; i++)
+    {
+      reg = inf->regs + i;
+
+      parent = reg->parents;
+      if (!parent)
+	continue;
+
+      /* Count the parent list.  */
+      while (*parent >= 0)
+	parent++;
+      nparents = parent - reg->parents;
+
+      /* Sort a copy of the parent list.  */
+      if (nparents > plen)
+	{
+	  plen = nparents;
+	  free (pbuf);
+	  pbuf = (int *) xmalloc (plen * sizeof (int));
+	}
+      memcpy (pbuf, reg->parents, (nparents + 1) * sizeof (int));
+      qsort (pbuf, nparents, sizeof (int), intcmp);
+
+      /* Add the register to each of its parents' child lists.  */
+      for (parent = pbuf; *parent >= 0; parent++)
+	{
+	  if (parent > pbuf && parent[-1] == *parent)
+	    continue;
+	  reg2 = find (parent, inf->tnums, inf->nregs, find_tnum_cmp);
+	  if (!reg2)
+	    internal_error ("init_children: no register %d", *parent);
+
+	  j = reg2 - inf->regs;
+	  if (!ccounts[j])
+	    reg2->children =
+	      (struct reg **) xmalloc (2 * sizeof (struct reg *));
+	  else
+	    reg2->children =
+	      (struct reg **) xrealloc (reg2->children, (ccounts[j] + 2)
+					* sizeof (struct reg *));
+	  reg2->children[ccounts[j]++] = reg;
+	}
+    }
+
+  /* Null-terminate dependent lists.  */
+  for (i = 0; i < inf->nregs; i++)
+    {
+      if (!ccounts[i])
+	inf->regs[i].children = NULL;
+      else
+	inf->regs[i].children[ccounts[i]] = NULL;
+    }
+
+  free (pbuf);
+  free (ccounts);
+}
+
+/* Begin initializing GDBARCH fields handled by this module.  Target modules
+   should call this, individual register definition functions like
+   regs_init_real(), and regs_init_finish() from their gdbarch_register()
+   callback.
+
+   Many quantities initialized here, e.g. num_regs, can't be initialized by a
+   register_gdbarch_data() callback because they must be initialized before
+   those callbacks get called.
+
+   CALLER_REGS (<frame>) is a callback for implementing saved register
+   lookups.  It should update <frame>'s caller register state from <frame>'s
+   to its caller's.  regs_get_caller(), regs_set_caller(), and
+   regs_copy_caller() can be used for querying and changing <frame>'s caller
+   register state.
+
+   If USE_GENERIC_DUMMY_FRAMES is true, then CALLER_REGS will never be passed
+   a dummy frame argument.  Otherwise, CALLER_REGS is responsible for noticing
+   dummy frames and copying saved dummy caller register state.  */
+
+struct regs_init_context *
+regs_init_start (struct gdbarch *gdbarch,
+		 void (*caller_regs) (struct frame_info *))
+{
+  struct regs_init_context *context;
+  struct inf *inf;
+
+  context = (struct regs_init_context *) xmalloc (sizeof *context);
+  context->gdbarch = gdbarch;
+  context->inf = inf = (struct inf *) xmalloc (sizeof (struct inf));
+
+  inf->caller_regs = caller_regs;
+  inf->regs = NULL;
+  inf->nregs = 0;
+  inf->nmregs = 0;
+
+  /* Prepare to accumulate various statistics.  */
+  inf->namemax = inf->cachesize = inf->rawmax = 0;
+  context->virtmax = 0;
+  context->maxtnum = -1;
+  context->regssz = 0;
+
+  return context;
+}
+
+/* Define in CONTEXT a register with attributes specified by the argument
+   list.  This is the back end for all public register definition
+   functions.  */
+
+static void
+init_any (struct regs_init_context *context, char *name, int tnum,
+	  int dnum, int size, struct type **type, CORE_ADDR mem,
+	  unsigned int flags, regs_rpseudo_ftype rpseudo,
+	  regs_wpseudo_ftype wpseudo, void *data, int *parents)
+{
+  struct inf *inf; struct reg *reg;
+  int namelen, i;
+
+  inf = context->inf;
+
+  /* Double the array size every time it reaches a power of 2.  */
+  if (context->regssz == 0)
+    {
+      context->regssz = 1;
+      inf->regs = (struct reg *) xmalloc (sizeof (struct reg));
+    }
+  else if (context->regssz == inf->nregs)
+    {
+      context->regssz *= 2;
+      inf->regs = (struct reg *) xrealloc (inf->regs, context->regssz
+					   * sizeof (struct reg));
+    }
+  reg = inf->regs + inf->nregs;
+
+  /* Copy attributes.  */
+  if (!name)
+    reg->name = NULL;
+  else
+    reg->name = strdup (name);
+  reg->tnum = tnum;
+  reg->dnum = dnum;
+  reg->size = size;
+  reg->type = type;
+  reg->mem = mem;
+  reg->flags = flags;
+  reg->rpseudo = rpseudo;
+  reg->wpseudo = wpseudo;
+  reg->data = data;
+
+  if (!parents)
+    reg->parents = NULL;
+  else
+    {
+      for (i = 0; parents[i] >= 0; i++)
+	;
+      reg->parents = (int *) xmalloc ((i + 1) * sizeof (int));
+      memcpy (reg->parents, parents, (i + 1) * sizeof (int));
+    }
+
+  /* Infer some flags.  */
+  if (mem != -1)
+    reg->flags |= REGS_MAPPED;
+
+  if (!name)
+    reg->flags |= REGS_HIDEALL;
+  if (reg->flags & REGS_HIDEALL)
+    reg->flags |= REGS_HIDESOME;
+
+  if (!rpseudo && !wpseudo)
+    reg->flags |= REGS_REAL;
+  else
+    reg->flags |= REGS_PSEUDO;
+
+  /* Accumulate various statistics.  */
+  inf->nregs++;
+  inf->cachesize += size;
+  if (name)
+    inf->nnregs++;
+  if (mem != -1)
+    inf->nmregs++;
+
+  /* Record maximum name length.  */
+  if (name)
+    {
+      namelen = strlen (name);
+      if (namelen > inf->namemax)
+	inf->namemax = namelen;
+    }
+
+  /* Notice maximum register number.  */
+  if (tnum > context->maxtnum)
+    context->maxtnum = tnum;
+
+  /* Record maximum sizes.  */
+  if (size > inf->rawmax)
+    inf->rawmax = size;
+  size = TYPE_LENGTH (*type);
+  if (size > context->virtmax)
+    context->virtmax = size;
+}
+
+/* Define a real register in CONTEXT.  */
+
+void
+regs_init_real (struct regs_init_context *context, char *name, int tnum,
+		int size, struct type **type, unsigned int flags)
+{
+  init_any (context, name, tnum, tnum, size, type, -1, flags,
+	    NULL, NULL, NULL, NULL);
+}
+
+/* Define a real vector register in CONTEXT.  */
+
+void
+regs_init_vec (struct regs_init_context *context, char *name, int tnum,
+	       int size, struct type **type, unsigned int flags)
+{
+  init_any (context, name, tnum, tnum, size, type, -1, flags,
+	    NULL, NULL, NULL, NULL);
+}
+
+/* Define a real memory-mapped register in CONTEXT.  */
+
+void
+regs_init_mem  (struct regs_init_context *context, char *name, int tnum,
+		int size, struct type **type, CORE_ADDR mem,
+		unsigned int flags)
+{
+  init_any (context, name, tnum, tnum, size, type, mem, flags,
+	    NULL, NULL, NULL, NULL);
+}
+
+/* Define a pseudo register in CONTEXT.  The register's GDB internal number
+   will be autogenerated later.  */
+
+void
+regs_init_pseudo (struct regs_init_context *context, char *name, int size,
+		  struct type **type, unsigned int flags,
+		  regs_rpseudo_ftype rpseudo, regs_wpseudo_ftype wpseudo,
+		  void *data, int *parents)
+{
+  init_any (context, name, 0, 0, size, type, -1, flags,
+	    rpseudo, wpseudo, data, parents);
+}
+
+/* Finish the initialization work started by regs_init_start().  */
+
+void
+regs_init_finish (struct regs_init_context *context)
+{
+  struct gdbarch *gdbarch;
+  struct inf *inf;
+
+  gdbarch = context->gdbarch;
+  inf = context->inf;
+
+  /* Free extra memory allocated for the register array.  */
+  inf->regs = (struct reg *) xrealloc (inf->regs, inf->nregs
+				       * sizeof (struct reg));
+
+  init_lookups (inf);
+  init_cache (inf);
+  init_pseudo (inf, context);
+  init_children (inf);
+
+  /* Set gdbarch fields.  */
+  set_gdbarch_register_list (gdbarch, inf);
+  set_gdbarch_num_regs (gdbarch, context->maxtnum + 1);
+  set_gdbarch_register_name (gdbarch, regs_register_name);
+  set_gdbarch_register_bytes (gdbarch, inf->cachesize);
+  set_gdbarch_register_byte (gdbarch, regs_register_byte);
+  set_gdbarch_register_raw_size (gdbarch, regs_register_raw_size);
+  set_gdbarch_max_register_raw_size (gdbarch, inf->rawmax);
+  set_gdbarch_register_virtual_size (gdbarch, regs_register_virtual_size);
+  set_gdbarch_max_register_virtual_size (gdbarch, context->virtmax);
+  set_gdbarch_register_virtual_type (gdbarch, regs_register_virtual_type);
+  set_gdbarch_init_extra_frame_info (gdbarch, regs_init_extra_frame_info);
+  set_gdbarch_frame_init_saved_regs (gdbarch, regs_frame_init_saved_regs);
+  set_gdbarch_get_saved_register (gdbarch, regs_get_saved_register);
+  set_gdbarch_pop_frame (gdbarch, regs_pop_frame);
+  set_gdbarch_frame_saved_pc (gdbarch, regs_frame_saved_pc);
+  set_gdbarch_frame_chain (gdbarch, regs_frame_chain);
+  set_gdbarch_fetch_pseudo_register (gdbarch, regs_fetch_pseudo_register);
+  set_gdbarch_store_pseudo_register (gdbarch, regs_store_pseudo_register);
+  set_gdbarch_do_registers_info (gdbarch, cliregs_info);
+
+  /* gdbarch_register_size()'s name makes it seem like a candidate for
+     autogeneration, but in fact it represents (as far as I can tell) the size
+     of an instruction, which isn't inferable from the information in struct
+     gdbreg.
+
+     It might make sense to add a .convertible field to struct gdbreg, for
+     initializing gdbarch_register_convertible().  Probably other fields
+     would be useful and could be added.  */
+
+  free (context);
+}
Index: gdb/regs.h
===================================================================
diff -up /dev/null gdb/regs.h
--- /dev/null	Sun Feb 12 03:29:56 1995
+++ gdb/regs.h	Tue Jan  2 22:49:48 2001
@@ -0,0 +1,290 @@
+/* Target register definition interface for GDB, the GNU debugger.
+   Copyright 2000 Free Software Foundation, Inc.
+
+   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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* This module implements a multi-arch interface for defining registers.  It
+   allows a multi-arch *-tdep.c file to specify one or more register sets for
+   one or more different architectures.
+
+   Sample usage:
+
+     context = regs_init_start (gdbarch, mips_caller_regs);
+     regs_init_real (context, "r0", 0, 4, &builtin_type_int32, 0);
+     regs_init_real (context, "r1", 1, 4, &builtin_type_int32, 0);
+     regs_init_real (context, "r2", 2, 4, &builtin_type_int32, 0);
+     ...
+     parents[0] = 2;
+     parents[1] = -1;
+     regs_init_pseudo (context, "r2_alias", 4, &builtin_type_int32,
+                       REGS_HIDEALL, regs_rpseudo_alias, regs_wpseudo_alias,
+                       parents);
+     ...
+     regs_init_finish (context);
+
+   Each defined register has the following attributes:
+
+     - name
+     - target number (aka "tnum")
+     - debug info number (aka "dnum")
+     - zero-based index number, determined by the order in which registers
+       are defined
+     - raw size
+     - virtual size
+     - virtual type
+     - offset in the cache buffer, which contains packed values sorted by
+       target number
+     - memory-mapped address
+     - pseudo-register read/write callbacks
+     - pseudo-register parent registers (dependencies)
+     - various flags
+
+   regs_init_real(), regs_init_mem(), and regs_init_pseudo() respectively
+   define real, memory-mapped, and pseudo registers with appropriate default
+   attributes.  Other register definition functions should be added as needed.
+
+   If cliregs_info() is used, "info registers" and "info all-registers"
+   sort registers in increasing order of index number.
+
+   regs_init_finish() installs callbacks for the following multi-arch
+   functions:
+
+     gdbarch_num_regs
+     gdbarch_num_pseudo_regs
+     gdbarch_register_name
+     gdbarch_register_bytes
+     gdbarch_register_byte
+     gdbarch_register_raw_size
+     gdbarch_max_register_raw_size
+     gdbarch_register_virtual_size
+     gdbarch_max_register_virtual_size
+     gdbarch_register_virtual_type
+     gdbarch_get_saved_register
+     gdbarch_pop_frame
+     gdbarch_frame_saved_pc
+     gdbarch_frame_chain
+     gdbarch_fetch_pseudo_register
+     gdbarch_store_pseudo_register
+     gdbarch_do_registers_info
+
+   Targets may install their own versions of any of those functions by calling
+   set_gdbarch_*() after regs_init_finish() returns.
+
+   Register target numbers are used as opaque identifiers when communicating
+   with remote stubs.  Therefore, to avoid breaking stubs, target numbers
+   should never change after they're chosen.
+
+   To define a pseudo-register, an architecture specifies a list of parent
+   registers and callbacks to read and write the pseudo-register.  This module
+   handles invalidating a pseudo-register when changing a parent register on
+   which the pseudo-register depends.
+
+   If a GDB user writes to the inferior's memory at a location where a
+   memory-mapped register is mapped, the user sees the new value when
+   examining the register.
+
+   Possible future enhancement: Targets might receive register blocks from the
+   hardware with padding between certain registers, in which case it might be
+   useful to add a pad field.
+
+   Current restrictions:
+     - register memory mapped locations must not overlap
+     - pseudo-register parents must be real registers */
+
+#ifndef GDB_REGS_H
+# define GDB_REGS_H
+
+/* Pseudo-register read and write callback types.  Read or write
+   pseudo-register REGNUM in FRAME to or from RAWBUF, reading or writing data
+   in the real registers in -1-terminated list PARENTS.
+
+   RAWBUF is guaranteed to be large enough to hold any register, and both read
+   and write callbacks may overwrite it, e.g. for temporary storage.
+
+   Return 1 on success, 0 on failure if a future attempt might succeed, -1
+   otherwise.  */
+
+typedef int (*regs_rpseudo_ftype) (struct frame_info *frame, int regnum,
+				   int *parents, void *data, char *rawbuf);
+typedef int (*regs_wpseudo_ftype) (struct frame_info *frame, int regnum,
+				   int *parents, void *data, char *rawbuf);
+
+/* Register flag bits.  */
+
+enum
+  {
+    REGS_HIDESOME = 0x01,	/* don't display in "info registers" */
+    REGS_HIDEALL = 0x02,	/* don't display in "info registers" or "info
+				   all-registers" */
+    REGS_RTHRU = 0x04,		/* don't cache reads */
+    REGS_WTHRU = 0x08,		/* don't cache writes (not implemented) */
+    REGS_REFFECT = 0x10,	/* reads have side-effects, so don't display
+				   during "info [all-]registers" unless
+				   explicitly requested */
+    REGS_MEMONLY = 0x20		/* access register through memory
+				   (memory-mapped registers only) */
+  };
+
+/* Start defining an architecture's registers.  */
+
+extern struct regs_init_context *
+regs_init_start (struct gdbarch *gdbarch,
+		 void (*caller_regs) (struct frame_info *));
+
+/* Define a real register.  */
+
+extern void regs_init_real (struct regs_init_context *context, char *name,
+			    int targnum, int size, struct type **type,
+			    unsigned int flags);
+
+/* Define a real memory-mapped register.  */
+
+extern void regs_init_mem (struct regs_init_context *context, char *name,
+			   int targnum, int size, struct type **type,
+			   CORE_ADDR memaddr, unsigned int flags);
+
+/* Define a pseudo-register.  */
+
+extern void regs_init_pseudo (struct regs_init_context *context, char *name,
+			      int size, struct type **type,
+			      unsigned int flags, regs_rpseudo_ftype rpseudo,
+			      regs_wpseudo_ftype wpseudo, void *data,
+			      int *parents);
+
+/* Finish defining an architecture's registers.  */
+
+extern void regs_init_finish (struct regs_init_context *context);
+
+/* Try to fetch register TNUM's value from FRAME into RAWBUF.  Return 1 on
+   success, 0 on failure if a future attempt might succeed, -1 otherwise.  */
+
+extern int regs_fetch_frame (struct frame_info *frame, int tnum, char *rawbuf);
+
+/* Try to store RAWBUF as register TNUM's value in FRAME.  Return
+   success.  */
+
+extern int regs_store_frame (struct frame_info *frame, int tnum, char *rawbuf);
+
+/* Return the value of register TNUM in FRAME.  */
+
+extern ULONGEST regs_get_frame (struct frame_info *frame, int tnum);
+
+/* Return the value of register TNUM in FRAME's caller.  */
+
+extern ULONGEST regs_get_caller (struct frame_info *frame, int tnum);
+
+/* Record the fact that register TNUM's value in FRAME's caller is:
+     - VALUE if LVAL is not_lval
+     - in memory location VALUE if LVAL is lval_memory
+     - in register TNUM if LVAL is lval_register  */
+
+extern void regs_set_caller (struct frame_info *frame, int tnum,
+			     enum lval_type lval, ULONGEST value);
+
+/* Copy register TNUM1's caller information in FRAME to register
+   TNUM2's.  */
+
+extern void regs_copy_caller (struct frame_info *frame, int tnum1, int tnum2);
+
+/* Translate internal numbers to various other register attributes.  */
+
+extern int regs_inum_tnum (int inum);
+extern char *regs_inum_name (int inum);
+extern struct type *regs_inum_type (int inum);
+extern int regs_inum_size (int inum);
+extern unsigned int regs_inum_flags (int inum);
+
+/* Invalidate any memory-mapped registers that the LEN-byte target memory
+   region starting at ADDR overlaps.  This function should be called whenever
+   target memory is written.  */
+
+extern void regs_memwrite (CORE_ADDR addr, int len);
+
+/* Return the target number of the register with LEN-byte NAME, or -1 if no
+   such register exists.  */
+
+extern int regs_name_tnum (char *name, int len);
+
+/* Return the target number of the register mapped to memory ADDR, or -1 if no
+   such register exists.  */
+
+extern int regs_addr_tnum (CORE_ADDR addr);
+
+/* Return the internal number of register TNUM.  */
+
+extern int regs_index (int tnum);
+
+/* Return >0 if register TNUM's value in the innermost frame is cached, 0 if
+   it's uncached but fetchable, and <0 if it's uncached and unfetchable.  */
+
+extern int regs_valid (int tnum);
+
+/* Record that TNUM's value is cached if STATE is >0, uncached but
+   fetchable if STATE is 0, and uncached and unfetchable if STATE is <0.
+
+   This function must be called whenever a register's cached state changes,
+   including when updating a valid value with a new valid value.  */
+
+extern void regs_set_valid (int tnum, int state);
+
+/* If TNUM >= 0, return a pointer to register TNUM's cache buffer area,
+   else return a pointer to the start of the cache buffer.  */
+
+extern char *regs_buffer (int tnum);
+
+/* Return the number of registers in the current architecture.  */
+
+extern int regs_count (void);
+
+/* Pseudo-register callbacks to implement register aliases.  */
+
+extern int regs_rpseudo_alias (struct frame_info *frame, int tnum,
+			       int *parents, void *data, char *rawbuf);
+extern int regs_wpseudo_alias (struct frame_info *frame, int tnum,
+			       int *parents, void *data, char *rawbuf);
+
+/* Pseudo-register callbacks to implement vector registers.  */
+
+extern int regs_rpseudo_vec (struct frame_info *frame, int tnum,
+			     int *parents, void *data, char *rawbuf);
+extern int regs_wpseudo_vec (struct frame_info *frame, int tnum,
+			     int *parents, void *data, char *rawbuf);
+
+/* Pseudo-register read callback to add all parent registers.  */
+
+extern int regs_rpseudo_sum (struct frame_info *frame, int tnum,
+			     int *parents, void *data, char *rawbuf);
+
+/* Pseudo-register write callback to subtract the second parent register
+   from the first.  */
+
+extern int regs_wpseudo_sub (struct frame_info *frame, int tnum,
+			     int *parents, void *data, char *rawbuf);
+
+/* Interface between regcache.c and regs.c.  */
+
+extern int regs_real (int);
+extern int regs_pseudo (int);
+
+/* gdbarch backward compatibility for non-multiarched targets.  */
+
+#ifndef REGISTER_LIST
+# define REGISTER_LIST 0
+#endif
+
+#endif    /* !GDB_REGS_H */
Index: gdb/target.c
===================================================================
diff -up gdb/target.c gdb/target.c
--- gdb/target.c	Wed Jan  3 15:38:40 2001
+++ gdb/target.c	Tue Jan  2 23:48:43 2001
@@ -30,6 +30,7 @@
 #include "bfd.h"
 #include "symfile.h"
 #include "objfiles.h"
+#include "regs.h"
 #include "gdb_wait.h"
 #include "dcache.h"
 #include <signal.h>
@@ -853,6 +854,10 @@ do_xfer_memory (CORE_ADDR memaddr, char 
   /* Zero length requests are ok and require no work.  */
   if (len == 0)
     return 0;
+
+  /* Deal with memory-mapped registers.  */
+  if (write)
+    regs_memwrite (memaddr, len);
 
   /* to_xfer_memory is not guaranteed to set errno, even when it returns
      0.  */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]