This is the mail archive of the
gdb@sources.redhat.com
mailing list for the GDB project.
Re: A better register interface
- To: ac131313 at cygnus dot com
- Subject: Re: A better register interface
- From: Nick Duffek <nsd at redhat dot com>
- Date: Thu, 15 Feb 2001 01:24:05 -0500
- CC: gdb at sources dot redhat dot com
- References: <3A8B5C60.92EEABA3@cygnus.com>
On 14-Feb-2001, Andrew Cagney wrote:
>As many people will tell you GDB's current register interface/model is
>so large you can comfortably drive a road train through it.
[...]
>Associated with this are a number of bugs. The most common are:
> o you can't get a complex pseudo
> register from a saved frame
> o you can't modify a complex pseudo
> o you can only print complex psuedos
> by a hack - have it backed by a
> read only buffer.
> o the RAW_REGISTER_BYTE() macro determines
> everything including the format of the
> remote G packet.
>(a complex pseudo is one that is constructed from random bits from the
>register cache or stack).
Here is the revamped register cache interface patch. It fixes all of the
above.
Since the last time I posted the patch, I modified it to address the
concerns you raised in January. Specifically:
On 13-Jan-2001, Andrew Cagney wrote:
>Looking at one of the functions:
> real_register (int regnum)
[...]
>I think these should be made virtual so that your, or any other, code
>can just drop in..
Done. I added several gdbarch.sh macros so that e.g. real_register() in
regcache.c is now REAL_REGISTER().
>I'm confused. Your saying that the CLI changes depend on regs.c? I
>think that is wrong. It should be possible to code the cli so that it
>doesn't specificly depend on regs.c.
I've re-coded it so that cli-regs.c can be used without regs.c.
On 14-Jan-2001, Andrew Cagney wrote:
>The gdbarch_data() or gdbarch_memory() stuff should do what you need.
>Just put the REGISTER_LIST in one of them.
Also done. I've made use of the new set_gdbarch_data() function to remove
REGISTER_LIST from gdbarch.sh.
There are no regressions on i686-pc-linux-gnu or d10v-unknown-elf.
It works well on 2 not-yet-public architectures, and several GDB engineers
have expressed interest in using it.
Okay to apply?
Nick
ChangeLog:
* Makefile.in (SUBDIR_CLI_OBS): Add cli-regs.o.
(SUBDIR_CLI_SRCS): Add cli/cli-regs.c.
(SFILES): Add regs.c.
(regs_h): New variable.
(COMMON_OBS): Add regs.o.
(INIT_FILES): Add $(SUBDIR_CLI_SRCS).
(regcache.o): Remove spurious trailing space.
(regs.o, cli-regs.o): New rules.
* cli/cli-regs.c: New file.
* cli/cli-regs.h: New file.
* defs.h (read_relative_register_raw_bytes): Move to value.h.
* findvar.c (value_of_register, value_from_register): Change
register_cached to REGISTER_CACHED.
* gdbarch.sh (NUM_REGS): Document.
(REGCACHE_MODULE_ACTIVE, REGISTER_NAME_TNUM, REGISTER_ADDR_TNUM,
REGISTER_CACHED, SET_REGISTER_CACHED, REGISTER_BUFFER,
REAL_REGISTER, PSEUDO_REGISTER, REGISTER_WRITE_MEMORY,
REGISTER_INFO_FIRST, REGISTER_INFO_NEXT, REGISTER_HIDESOME,
REGISTER_HIDEALL, REGISTER_RDONLY, REGISTER_REFFECT,
FETCH_FRAME_REGISTER): New macros.
* gdbarch.c: Regenerate.
* gdbarch.h: Regenerate.
* parse.c (target_map_name_to_register): Call
REGISTER_NAME_TNUM() if available.
* regcache.c: (set_register_cached): Update documentation.
(register_changed, registers_changed, registers_fetched,
fetch_register, store_register, read_register_bytes,
read_relative_register_raw_bytes_for_frame,
read_register_gen, write_register_gen, read_register,
read_signed_register, write_register, supply_register): Uppercase
register_cached, set_register_cached, pseudo_register,
real_register, register_buffer.
(read_relative_register_raw_bytes_for_frame, real_register,
pseudo_register): Don't declare as static.
(register_info_first, register_info_next, register_hidesome,
register_hideall, register_rdonly, register_reffect,
register_memonly, fetch_frame_register): New functions.
(fetch_register): Issue error if neither real nor pseudo.
(store_register): Likewise, and call REGISTER_RDONLY.
(write_register_bytes): Call REGISTER_BUFFER instead of
dereferencing registers[].
(build_regcache): Omit initialization if !REGCACHE_MODULE_ACTIVE.
* regs.c: New file.
* regs.h: New file.
* remote.c (supply_them): Uppercase set_register_cached.
(store_register_using_P, remote_store_registers): Uppercase
register_buffer.
* target.c (do_xfer_memory): Call REGISTER_WRITE_MEMORY if
available.
* value.h (real_register, pseudo_register, register_info_first,
register_info_next, register_hidesome, register_hideall,
register_rdonly, register_reffect, register_memonly,
fetch_frame_register, read_relative_register_raw_bytes,
read_relative_register_raw_bytes_for_frame): Prototype.
Index: gdb/Makefile.in
===================================================================
diff -up gdb/Makefile.in gdb/Makefile.in
--- gdb/Makefile.in Thu Feb 15 00:51:07 2001
+++ gdb/Makefile.in Tue Feb 13 23:09:38 2001
@@ -141,10 +141,11 @@ 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-utils.o
+ cli-decode.o cli-script.o cli-cmds.o cli-setshow.o cli-utils.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-utils.c
+ cli/cli-utils.c cli/cli-regs.c
SUBDIR_CLI_DEPS =
SUBDIR_CLI_INITS =
SUBDIR_CLI_LDFLAGS=
@@ -519,6 +520,7 @@ SFILES = ax-general.c ax-gdb.c bcache.c
kod.c kod-cisco.c \
ui-out.c cli-out.c \
varobj.c wrapper.c \
+ regs.c \
jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c \
m2-exp.y m2-lang.c m2-typeprint.c m2-valprint.c main.c maint.c \
memattr.c mem-break.c minsyms.c mipsread.c nlmread.c objfiles.c \
@@ -600,12 +602,14 @@ 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_utils_h = $(srcdir)/cli/cli-utils.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
@@ -659,6 +663,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 \
@@ -781,7 +786,7 @@ uninstall: force $(CONFIG_UNINSTALL)
# tui-file.c.
#
-INIT_FILES = $(OBS) $(TSOBS) $(CONFIG_OBS) $(CONFIG_INITS)
+INIT_FILES = $(OBS) $(SUBDIR_CLI_SRCS) $(TSOBS) $(CONFIG_OBS) $(CONFIG_INITS)
init.c: $(INIT_FILES)
@echo Making init.c
@rm -f init.c-tmp init.l-tmp
@@ -1342,7 +1347,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.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
@@ -2086,6 +2094,11 @@ cli-script.o: $(srcdir)/cli/cli-script.c
cli-utils.o: $(srcdir)/cli/cli-utils.c $(cli_utils_h) $(defs_h)
$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/cli/cli-utils.c
+
+cli-regs.o: $(srcdir)/cli/cli-regs.c $(cli_regs_h) \
+ $(defs_h) $(floatformat_h) $(frame_h) $(value_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 Tue Feb 13 23:39:34 2001
@@ -0,0 +1,725 @@
+/* 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 "cli/cli-utils.h"
+#include "floatformat.h"
+#include "ui-file.h"
+#include "symcat.h"
+#include "value.h"
+#include "frame.h"
+
+/* Minimum space between register name and values. */
+
+#define NAMEGAP 2
+
+/* Minimum space between register hexadecimal and decimal/float values. */
+
+#define HEXGAP 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 "<error>"
+#define DISP_EFFECT "<live>"
+
+/* Indices into 2-element arrays representing "info all-registers" and "info
+ registers" formatting information. */
+
+#define SOME 0
+#define ALL 1
+
+/* Information about a register's value component display format. */
+
+struct compfmt
+ {
+ unsigned int right : 1; /* whether to right-justify instead of
+ left-justify the component */
+ unsigned int width : 15; /* width of justified component */
+ unsigned int gap : 15; /* number of spaces to emit after the
+ component if not wrapped */
+ };
+
+/* Information about a register's value display format. */
+
+struct valfmt
+ {
+ struct reg *reg; /* register to which this format applies */
+ int ncomps; /* number of value components */
+ void (*display) /* callback to display a component */
+ (struct reg *, int, char *, char *);
+ int width; /* total width needed to display all value
+ components */
+ struct compfmt *compfmts; /* value component formats */
+ };
+
+/* Information about a register's display format in "info all-registers" or
+ "info registers" context. */
+
+struct ctxtfmt
+ {
+ unsigned int hide : 1; /* whether to hide the register in this
+ context */
+ unsigned int namepad : 10; /* number of spaces before the name */
+ unsigned int newline : 1; /* whether to emit a newline after the
+ last value component */
+ unsigned int gap : 10; /* number of spaces after the last value
+ component */
+ unsigned int namewrap : 10; /* number of spaces to emit after wrapping
+ between components */
+ };
+
+/* Per-register information. */
+
+struct reg
+ {
+ int tnum; /* register's target number */
+ struct type *type; /* register's type */
+ int size; /* register's size */
+ struct valfmt *valfmt; /* value formattting information */
+ struct ctxtfmt ctxtfmt[2]; /* context-specific formatting information */
+ };
+
+/* Per-architecture information for internal use by this module. */
+
+struct inf
+ {
+ struct reg *regs; /* per-register information, in display
+ order */
+ int nregs; /* length of .regs */
+ struct valfmt *valfmts; /* value formats referenced by .regs */
+ int nvalfmts; /* length of .valfmts */
+ char *virtbuf; /* for storing any virtual register value */
+ int prev_screenw; /* screen width during most recent
+ cliregs_info() call */
+ };
+
+/* Memory file buffer for formatting, needed for padding the results of
+ val_print(). */
+
+static struct ui_file *memfile = NULL;
+
+/* 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
+display_val (struct type *type, char *buf, int format, int right, int width)
+{
+ char *memstr;
+ long len;
+ struct cleanup *cleanups;
+
+ /* Clear the buffer passed to val_print(), creating it first if needed. */
+ if (!memfile)
+ memfile = mem_fileopen ();
+ else
+ ui_file_rewind (memfile);
+
+ val_print (type, buf, 0, 0, memfile, format, 1, 0, Val_pretty_default);
+
+ memstr = ui_file_xstrdup (memfile, &len);
+ cleanups = make_cleanup (xfree, memstr);
+
+ printf_filtered (right ? "%*s" : "%-*s", width, memstr);
+ do_cleanups (cleanups);
+}
+
+/* Display component number I of vector register REG. */
+
+static void
+display_vec (struct reg *reg, int i, char *rawbuf, char *virtbuf)
+{
+ struct type *type;
+ int elen;
+
+ /* Print components in hex. */
+ type = TYPE_TARGET_TYPE (reg->type);
+ elen = TYPE_LENGTH (type);
+
+ display_val (type, virtbuf + i * elen, 'x', reg->valfmt->compfmts[i].right,
+ reg->valfmt->compfmts[i].width);
+}
+
+/* Display component number I of normal register REG. */
+
+static void
+display_hexdec (struct reg *reg, int i, char *rawbuf, char *virtbuf)
+{
+ /* Print non-floats in hex and decimal. */
+ display_val (reg->type, virtbuf, i ? 'd' : 'x', reg->valfmt->compfmts[i].right,
+ reg->valfmt->compfmts[i].width);
+}
+
+/* Display component number I of float register REG. */
+
+static void
+display_float (struct reg *reg, int i, char *rawbuf, char *virtbuf)
+{
+ int j;
+ char *memstr;
+ long len;
+ struct cleanup *cleanups;
+
+ /* Print floats as numbers and hex strings. */
+ if (!i)
+ {
+#ifdef INVALID_FLOAT
+ if (INVALID_FLOAT (virtbuf, TYPE_LENGTH (reg->type)))
+ printf_filtered ("%-*s", reg->valfmt->width, DISP_INVFLOAT);
+ else
+#endif
+ display_val (reg->type, virtbuf, 0, reg->valfmt->compfmts[i].right,
+ reg->valfmt->compfmts[i].width);
+ }
+ else
+ {
+ if (!memfile)
+ memfile = mem_fileopen ();
+ else
+ ui_file_rewind (memfile);
+
+ fputs_unfiltered ("(raw 0x", memfile);
+ for (j = 0; j < reg->size; j++)
+ {
+ int idx = TARGET_BYTE_ORDER == BIG_ENDIAN ? j
+ : reg->size - 1 - j;
+ fprintf_unfiltered (memfile, "%02x", (unsigned char) rawbuf[idx]);
+ }
+ fputc_unfiltered (')', memfile);
+
+ memstr = ui_file_xstrdup (memfile, &len);
+ cleanups = make_cleanup (xfree, memstr);
+
+ puts_filtered (memstr);
+ do_cleanups (cleanups);
+ }
+}
+
+/* Return the index in INF->valfmts of the format to be used for REG, creating
+ the format if it doesn't exist.
+
+ FIXME: this should call methods in struct language_defn. */
+
+static int
+init_valfmt (struct inf *inf, struct reg *reg)
+{
+ int len, slen, elen, i;
+ const char *unavail[] = { DISP_NOVAL, DISP_EFFECT, NULL };
+ struct valfmt *valfmt;
+ enum type_code code, code2;
+ struct compfmt *compfmts;
+
+ len = TYPE_LENGTH (reg->type);
+ code = TYPE_CODE (reg->type);
+
+ /* Search for a format for TYPE and SIZE in INF->valfmts. This uses
+ in-depth knowledge of how all values are formatted, so it'll probably
+ become obsolete if this function is ever fixed to call language_defn
+ methods.
+
+ Running time for this search is O(n^2), where n is the number of register
+ formats in the current architecture. */
+ for (i = 0; i < inf->nvalfmts; i++)
+ {
+ valfmt = inf->valfmts + i;
+
+ /* Length and raw size must match. */
+ if (valfmt->reg->size != reg->size)
+ continue;
+ if (len != TYPE_LENGTH (valfmt->reg->type))
+ continue;
+
+ /* Float matches float, vector matches vector, anything else matches
+ anything else. */
+ code2 = TYPE_CODE (valfmt->reg->type);
+ if (code != code2)
+ {
+ if (code == TYPE_CODE_FLT || code2 == TYPE_CODE_FLT)
+ continue;
+ if (code == TYPE_CODE_ARRAY || code2 == TYPE_CODE_ARRAY)
+ continue;
+ }
+
+ /* Vector element lengths must match. */
+ else if (code == TYPE_CODE_ARRAY
+ && (TYPE_LENGTH (TYPE_TARGET_TYPE (reg->type))
+ != TYPE_LENGTH (TYPE_TARGET_TYPE (valfmt->reg->type))))
+ continue;
+
+ /* Found a matching format. */
+ return i;
+ }
+
+ /* Create a new format. */
+ inf->nvalfmts++;
+ valfmt = inf->valfmts + inf->nvalfmts - 1;
+ valfmt->reg = reg;
+
+ /* Vectors get printed as a VECGAP-separated hexadecimal list. */
+ if (code == TYPE_CODE_ARRAY)
+ {
+ elen = TYPE_LENGTH (TYPE_TARGET_TYPE (reg->type));
+ valfmt->ncomps = len / elen;
+ valfmt->compfmts = compfmts = xmalloc (valfmt->ncomps
+ * sizeof *valfmt->compfmts);
+ for (i = 0; i < valfmt->ncomps; i++)
+ {
+ compfmts[i].right = 1;
+ compfmts[i].width = 2 + elen * 2;
+ compfmts[i].gap = VECGAP;
+ }
+ valfmt->width = (2 + elen * 2) * valfmt->ncomps
+ + VECGAP * (valfmt->ncomps - 1);
+ valfmt->display = display_vec;
+ }
+
+ /* Floats get printed as floats followed by "(raw <hex>)". */
+ else if (code == TYPE_CODE_FLT)
+ {
+ valfmt->ncomps = 2;
+ valfmt->compfmts = compfmts = xmalloc (2 * sizeof *valfmt->compfmts);
+
+ /* print_floating() gives floats more digits than they need, so use its
+ hard-coded values rather than inferring from float width. FIXME:
+ fix print_floating(), then change this accordingly. */
+
+ compfmts[0].right = 0;
+ compfmts[0].gap = HEXGAP;
+
+ if (len < sizeof (double))
+ compfmts[0].width = 11;
+ else if (len == sizeof (double))
+ compfmts[0].width = 19;
+#ifdef PRINTF_HAS_LONG_DOUBLE
+ else
+ compfmts[0].width = 37;
+#else
+ else
+ compfmts[0].width = 19;
+#endif
+
+ compfmts[1].right = 0;
+ compfmts[1].width = (8 + /* (raw 0x) */
+ reg->size * 2); /* 2 hex digits per byte */
+
+ valfmt->width = compfmts[0].width + HEXGAP + compfmts[1].width;
+ slen = sizeof (DISP_INVFLOAT) - 1;
+ if (valfmt->width < slen)
+ valfmt->width = slen;
+
+ valfmt->display = display_float;
+ }
+
+ /* Everything else gets printed in hex followed by signed decimal. */
+ else
+ {
+ valfmt->ncomps = 2;
+ valfmt->compfmts = compfmts = xmalloc (2 * sizeof *valfmt->compfmts);
+
+ compfmts[0].right = 1;
+ compfmts[0].gap = HEXGAP;
+ compfmts[0].width = (2 + /* 0x */
+ len * 2); /* 2 hex digits per byte */
+
+ compfmts[1].right = 1;
+ compfmts[1].width = 1; /* sign */
+ if (len == 1)
+ compfmts[1].width += 3; /* 3 digits for 1-byte value */
+ else
+ compfmts[1].width += len * 5 / 2; /* poor man's base-10 logarithm */
+
+ valfmt->width = compfmts[0].width + HEXGAP + compfmts[1].width;
+
+ valfmt->display = display_hexdec;
+ }
+
+ /* Unavailable-value strings. */
+ for (i = 0; unavail[i]; i++)
+ if (valfmt->width < (len = strlen (unavail[i])))
+ valfmt->width = len;
+
+ return inf->nvalfmts - 1;
+}
+
+/* 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 value format.
+
+ (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, *reg_valfmti, *valfmti_namemax[2], namelen, valfmti;
+ struct reg *reg;
+ char *name;
+
+ inf->nvalfmts = 0;
+
+ /* Register display formats. */
+ inf->valfmts = xmalloc (inf->nregs * sizeof (struct valfmt));
+
+ /* Format indices versus maximum name lengths, for implementing both (1)
+ and (2). */
+ for (i = SOME; i <= ALL; i++)
+ valfmti_namemax[i] = xcalloc (inf->nregs, sizeof (int));
+
+ /* Register indices versus format numbers, for caching init_valfmt(). */
+ reg_valfmti = xmalloc (inf->nregs * sizeof (int));
+
+ for (i = SOME; i <= ALL; i++)
+ {
+ /* (1) and (2): create subsets, calculate max name lengths. */
+ for (j = 0; j < inf->nregs; j++)
+ {
+ reg = inf->regs + j;
+
+ if (REGISTER_HIDEALL (reg->tnum))
+ reg->ctxtfmt[i].hide = 1;
+ else if (i == SOME && REGISTER_HIDESOME (reg->tnum))
+ reg->ctxtfmt[i].hide = 1;
+ else
+ reg->ctxtfmt[i].hide = 0;
+
+ if (i == ALL)
+ valfmti = reg_valfmti[j];
+ else
+ reg_valfmti[j] = valfmti = init_valfmt (inf, reg);
+
+ if (!reg->ctxtfmt[i].hide)
+ {
+ namelen = strlen (REGISTER_NAME (reg->tnum));
+ if (namelen > valfmti_namemax[i][valfmti])
+ valfmti_namemax[i][valfmti] = namelen;
+ }
+ }
+ }
+
+ /* Free unused space. */
+ inf->valfmts = xrealloc (inf->valfmts, inf->nvalfmts
+ * sizeof (struct valfmt));
+
+ for (i = SOME; i <= ALL; i++)
+ {
+ /* (1) and (3): finish creating subsets, pad register names. */
+ for (j = 0; j < inf->nregs; j++)
+ {
+ reg = inf->regs + j;
+ valfmti = reg_valfmti[j];
+ reg->valfmt = inf->valfmts + valfmti;
+
+ /* Pad register name. */
+ if (reg->ctxtfmt[i].hide)
+ {
+ reg->ctxtfmt[i].namepad = 0;
+ reg->ctxtfmt[i].namewrap = 0;
+ }
+ else
+ {
+ name = REGISTER_NAME (reg->tnum);
+ reg->ctxtfmt[i].namepad =
+ valfmti_namemax[i][valfmti] - strlen (name);
+ reg->ctxtfmt[i].namewrap =
+ 1 + valfmti_namemax[i][valfmti] + NAMEGAP;
+ }
+ }
+ }
+
+ for (i = SOME; i <= ALL; i++)
+ free (valfmti_namemax[i]);
+ free (reg_valfmti);
+}
+
+/* Update INF for screen width SCREENW. FIXME: arrange for this to be called
+ from cliregs_info() if the screen width has changed. */
+
+static void
+set_screenw (struct inf *inf, int screenw)
+{
+ int i, j, *valfmti_ncols, valfmti_prev, valfmti, col, width;
+ struct reg *reg, *reg_prev;
+
+ /* Format indices versus column counts, for implementing (4). */
+ valfmti_ncols = xcalloc (inf->nvalfmts, sizeof (int));
+
+ for (i = SOME; i <= ALL; i++)
+ {
+ /* (4): Calculate columns. */
+ reg_prev = NULL;
+ valfmti_prev = -1;
+ col = 0;
+
+ for (j = 0; j < inf->nregs; j++)
+ {
+ reg = inf->regs + j;
+ if (reg->ctxtfmt[i].hide)
+ {
+ reg->ctxtfmt[i].newline = 1;
+ reg->ctxtfmt[i].gap = 0;
+ continue;
+ }
+
+ valfmti = reg->valfmt - inf->valfmts;
+ if (!valfmti_ncols[valfmti])
+ {
+ width = reg->ctxtfmt[i].namewrap + reg->valfmt->width;
+ valfmti_ncols[valfmti] = (screenw + COLGAP) / (width + COLGAP);
+ if (valfmti_ncols[valfmti] <= 0)
+ valfmti_ncols[valfmti] = 1;
+ }
+
+ /* Start a new line if the value won't fit on the current one or if
+ its format differs from the previous register value's. */
+ if (valfmti_prev != valfmti || col++ == valfmti_ncols[valfmti])
+ col = 1;
+ else
+ {
+ reg_prev->ctxtfmt[i].newline = 0;
+ reg_prev->ctxtfmt[i].gap = COLGAP;
+ }
+
+ reg->ctxtfmt[i].newline = 1;
+ reg->ctxtfmt[i].gap = 0;
+ valfmti_prev = valfmti;
+ reg_prev = reg;
+ }
+ }
+
+ free (valfmti_ncols);
+}
+
+/* Initialize INF->regs and INF->nregs. */
+
+static void
+init_regs (struct inf *inf)
+{
+ struct reg *reg;
+ int tnum;
+ char *name;
+
+ inf->regs = xmalloc ((NUM_REGS + NUM_PSEUDO_REGS) * sizeof (struct reg));
+ inf->nregs = 0;
+
+ for (tnum = REGISTER_INFO_FIRST (); tnum >= 0;
+ tnum = REGISTER_INFO_NEXT (tnum))
+ {
+ name = REGISTER_NAME (tnum);
+ if (!name || !*name)
+ continue;
+
+ reg = inf->regs + inf->nregs++;
+ reg->tnum = tnum;
+ reg->type = REGISTER_VIRTUAL_TYPE (tnum);
+ reg->size = REGISTER_RAW_SIZE (tnum);
+ }
+
+ /* Free unused memory. */
+ inf->regs = xrealloc (inf->regs, inf->nregs * sizeof (struct reg));
+}
+
+/* Update INF to reflect the current screen size, initializing INF first if
+ necessary. */
+
+static void
+update_inf (struct inf *inf)
+{
+ int init, screenw;
+
+ if ((init = !inf->regs))
+ {
+ init_regs (inf);
+ init_columns (inf);
+ inf->virtbuf = xmalloc (MAX_REGISTER_VIRTUAL_SIZE);
+ }
+
+ screenw = SCREENW;
+ if (init || screenw != inf->prev_screenw)
+ {
+ set_screenw (inf, SCREENW);
+ inf->prev_screenw = screenw;
+ }
+}
+
+/* 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 value format are distributed across the
+ same number of columns, within which values are vertically aligned.
+
+ - A multi-value register is displayed starting on a new line if some but
+ not all of its values will fit on the previous line. */
+
+static void
+cliregs_info (int tnum, int all)
+{
+ int i, j, from, to, multi, width, gap, offset;
+ struct reg *reg;
+ struct inf *inf;
+ struct ctxtfmt *ctxtfmt;
+ char *rawbuf;
+
+ inf = gdbarch_data (inf_id);
+ update_inf (inf);
+
+ rawbuf = (char *) alloca (MAX_REGISTER_RAW_SIZE);
+ multi = tnum == -1;
+
+ /* Display registers. */
+ for (i = 0; i < inf->nregs; i++)
+ {
+ reg = inf->regs + i;
+ if (!multi && reg->tnum != tnum)
+ continue;
+
+ if (multi)
+ ctxtfmt = ®->ctxtfmt[all ? ALL : SOME];
+ else
+ ctxtfmt = ®->ctxtfmt[REGISTER_HIDESOME (reg->tnum) ? ALL : SOME];
+
+ if (multi && ctxtfmt->hide)
+ continue;
+
+ /* Display name. */
+ printf_filtered ("%-*s$%s%" XSTRING (NAMEGAP) "s",
+ ctxtfmt->namepad, "", REGISTER_NAME (reg->tnum), "");
+ offset = ctxtfmt->namewrap;
+
+ /* Don't display registers with side-effects unless explicitly
+ requested. */
+ if (multi && REGISTER_REFFECT (reg->tnum))
+ {
+ puts_filtered (DISP_EFFECT);
+ offset += strlen (DISP_EFFECT);
+ }
+
+ /* Try to retrieve raw value. */
+ else if (FETCH_FRAME_REGISTER (selected_frame, reg->tnum, rawbuf) <= 0)
+ {
+ puts_filtered (DISP_NOVAL);
+ offset += strlen (DISP_NOVAL);
+ }
+
+ /* Display the retrieved value. */
+ else
+ {
+ /* Convert to virtual. */
+ if (!REGISTER_CONVERTIBLE (reg->tnum))
+ memcpy (inf->virtbuf, rawbuf, TYPE_LENGTH (reg->type));
+ else
+ REGISTER_CONVERT_TO_VIRTUAL (reg->tnum, reg->type, rawbuf,
+ inf->virtbuf);
+
+ /* Display value components. */
+ for (j = 0; j < reg->valfmt->ncomps; j++)
+ {
+ width = reg->valfmt->compfmts[j].width;
+ if (j)
+ {
+ gap = reg->valfmt->compfmts[j - 1].gap;
+ if (offset + gap + width > SCREENW)
+ {
+ printf_filtered ("\n%*s", ctxtfmt->namewrap, "");
+ offset = ctxtfmt->namewrap;
+ }
+ else
+ {
+ printf_filtered ("%*s", gap, "");
+ offset += gap;
+ }
+ }
+ reg->valfmt->display (reg, j, rawbuf, inf->virtbuf);
+ offset += width;
+ }
+ }
+
+ /* Print column gap or newline. */
+ if (ctxtfmt->newline || !multi)
+ putchar_filtered ('\n');
+ else
+ {
+ gap = ctxtfmt->gap;
+ offset -= ctxtfmt->namewrap;
+ if (offset < reg->valfmt->width)
+ gap += reg->valfmt->width - offset;
+ printf_filtered ("%*s", gap, "");
+ }
+ }
+}
+
+/* Initialize GDBARCH fields handled by this module. Architectures should
+ call this function from their gdbarch_register() callbacks. */
+
+void
+cliregs_init (struct gdbarch *gdbarch)
+{
+ struct inf *inf;
+
+ /* To avoid allocating space for architectures not using cliregs_info(),
+ assign GDBARCH's struct inf here rather than via a
+ register_gdbarch_data() callback.
+
+ struct inf initialization requires gdbarch.sh macros that require a valid
+ current_gdbarch. Therefore, postpone initialization until the first
+ cliregs_info() call. */
+
+ inf = xmalloc (sizeof (struct inf));
+ inf->regs = NULL;
+ set_gdbarch_data (gdbarch, inf_id, inf);
+
+ set_gdbarch_do_registers_info (gdbarch, cliregs_info);
+}
+
+/* Module initialization. */
+
+void
+_initialize_cliregs (void)
+{
+ inf_id = register_gdbarch_data (NULL, NULL);
+}
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 Tue Feb 13 23:09:38 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_REGS_H)
+# define CLI_REGS_H 1
+
+/* Architecture initialization routine. */
+
+extern void cliregs_init (struct gdbarch *);
+
+#endif /* !defined (CLI_REGS_H) */
Index: gdb/defs.h
===================================================================
diff -up gdb/defs.h gdb/defs.h
--- gdb/defs.h Thu Feb 15 00:51:26 2001
+++ gdb/defs.h Tue Feb 13 23:09:38 2001
@@ -584,10 +584,6 @@ extern void exec_set_section_offsets (bf
bfd_signed_vma data_off,
bfd_signed_vma bss_off);
-/* From findvar.c */
-
-extern int read_relative_register_raw_bytes (int, char *);
-
/* Possible lvalue types. Like enum language, this should be in
value.h, but needs to be here for the same reason. */
Index: gdb/findvar.c
===================================================================
diff -up gdb/findvar.c gdb/findvar.c
--- gdb/findvar.c Thu Feb 15 00:51:34 2001
+++ gdb/findvar.c Tue Feb 13 23:09:38 2001
@@ -401,7 +401,7 @@ value_of_register (int regnum)
get_saved_register (raw_buffer, &optim, &addr,
selected_frame, regnum, &lval);
- if (register_cached (regnum) < 0)
+ if (REGISTER_CACHED (regnum) < 0)
return NULL; /* register value not available */
reg_val = allocate_value (REGISTER_VIRTUAL_TYPE (regnum));
@@ -795,7 +795,7 @@ value_from_register (struct type *type,
page_regnum,
&lval);
- if (register_cached (page_regnum) == -1)
+ if (REGISTER_CACHED (page_regnum) == -1)
return NULL; /* register value not available */
if (lval == lval_register)
@@ -812,7 +812,7 @@ value_from_register (struct type *type,
regnum,
&lval);
- if (register_cached (regnum) == -1)
+ if (REGISTER_CACHED (regnum) == -1)
return NULL; /* register value not available */
if (lval == lval_register)
@@ -838,7 +838,7 @@ value_from_register (struct type *type,
local_regnum,
&lval);
- if (register_cached (local_regnum) == -1)
+ if (REGISTER_CACHED (local_regnum) == -1)
return NULL; /* register value not available */
if (regnum == local_regnum)
@@ -904,7 +904,7 @@ value_from_register (struct type *type,
get_saved_register (raw_buffer, &optim, &addr, frame, regnum, &lval);
- if (register_cached (regnum) == -1)
+ if (REGISTER_CACHED (regnum) == -1)
return NULL; /* register value not available */
VALUE_OPTIMIZED_OUT (v) = optim;
Index: gdb/gdbarch.sh
===================================================================
diff -up gdb/gdbarch.sh gdb/gdbarch.sh
--- gdb/gdbarch.sh Thu Feb 15 00:52:19 2001
+++ gdb/gdbarch.sh Tue Feb 13 23:09:38 2001
@@ -358,7 +358,7 @@ f::TARGET_READ_FP:CORE_ADDR:read_fp:void
f::TARGET_WRITE_FP:void:write_fp:CORE_ADDR val:val::0:generic_target_write_fp::0
f::TARGET_READ_SP:CORE_ADDR:read_sp:void:::0:generic_target_read_sp::0
f::TARGET_WRITE_SP:void:write_sp:CORE_ADDR val:val::0:generic_target_write_sp::0
-#
+# Number of real (not pseudo) registers.
v:2:NUM_REGS:int:num_regs::::0:-1
# This macro gives the number of pseudo-registers that live in the
# register namespace but do not get fetched or stored on the target.
@@ -392,6 +392,54 @@ 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
+# Whether regcache.c's register cache is active.
+v:2:REGCACHE_MODULE_ACTIVE:int:regcache_module_active:::::1::0
+# Return the target number of the register with LEN-byte NAME, or -1 if no
+# such register exists.
+F:2:REGISTER_NAME_TNUM:int:register_name_tnum:char *name, int len:name, len::0
+# Return the target number of the register mapped to memory ADDR, or -1 if no
+# such register exists.
+F:2:REGISTER_ADDR_TNUM:int:register_addr_tnum:CORE_ADDR addr:addr::0
+# Return >0 if register REGNUM's value in the innermost frame is cached, 0 if
+# it's uncached but fetchable, and <0 if it's uncached and unfetchable.
+f:2:REGISTER_CACHED:int:register_cached:int regnum:regnum:::register_cached::0
+# 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.
+f:2:SET_REGISTER_CACHED:void:set_register_cached:int regnum, int state:regnum, state:::set_register_cached::0
+# If REGNUM >= 0, return a pointer to register REGNUM's cache buffer area,
+# else return a pointer to the start of the cache buffer.
+f:2:REGISTER_BUFFER:char *:register_buffer:int regnum:regnum:::register_buffer::0
+# Return whether register REGNUM is a real (not pseudo) register.
+f:2:REAL_REGISTER:int:real_register:int regnum:regnum:::real_register::0
+# Return whether register REGNUM is a pseudo register.
+f:2:PSEUDO_REGISTER:int:pseudo_register:int regnum:regnum:::pseudo_register::0
+# 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.
+F:2:REGISTER_WRITE_MEMORY:void:register_write_memory:CORE_ADDR addr, int len:addr, len::0
+# Return the target number of the first register that "info registers" and
+# "info all-registers" should consider for display.
+f:2:REGISTER_INFO_FIRST:int:register_info_first:void::::register_info_first::0
+# Return the target number of the register that "info registers" and "info
+# all-registers" should consider for display after register REGNUM. If no
+# registers should be displayed after register REGNUM, return -1.
+f:2:REGISTER_INFO_NEXT:int:register_info_next:int regnum:regnum:::register_info_next::0
+# Return whether register REGNUM should be omitted from "info registers"
+# display.
+f:2:REGISTER_HIDESOME:int:register_hidesome:int regnum:regnum:::register_hidesome::0
+# Return whether register REGNUM should be omitted from "info registers"
+# and "info all-registers" display.
+f:2:REGISTER_HIDEALL:int:register_hideall:int regnum:regnum:::register_hideall::0
+# Return whether register REGNUM is read-only.
+f:2:REGISTER_RDONLY:int:register_rdonly:int regnum:regnum:::register_rdonly::0
+# Return whether side-effects result from reading register REGNUM.
+f:2:REGISTER_REFFECT:int:register_reffect:int regnum:regnum:::register_reffect::0
+# Try to fetch register REGNUM's value from FRAME into RAWBUF. Return 1 on
+# success, 0 on failure if a future attempt might succeed, -1 otherwise.
+f:2:FETCH_FRAME_REGISTER:int:fetch_frame_register:struct frame_info *frame, int regnum, char *rawbuf:frame, regnum, rawbuf:::fetch_frame_register::0
# 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 Thu Feb 15 00:52:25 2001
+++ gdb/parse.c Tue Feb 13 23:09:38 2001
@@ -116,6 +116,9 @@ target_map_name_to_register (char *str,
{
int i;
+ if (REGISTER_NAME_TNUM_P ())
+ return REGISTER_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
multiprocessor can be could be any of several PCs). */
Index: gdb/regcache.c
===================================================================
diff -up gdb/regcache.c gdb/regcache.c
--- gdb/regcache.c Thu Feb 15 00:52:34 2001
+++ gdb/regcache.c Wed Feb 14 01:30:47 2001
@@ -70,7 +70,10 @@ register_cached (int 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.
+
+ This function must 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)
@@ -84,7 +87,7 @@ set_register_cached (int regnum, int sta
void
register_changed (int regnum)
{
- set_register_cached (regnum, 0);
+ SET_REGISTER_CACHED (regnum, 0);
}
/* If REGNUM >= 0, return a pointer to register REGNUM's cache buffer area,
@@ -99,9 +102,9 @@ register_buffer (int regnum)
return ®isters[REGISTER_BYTE (regnum)];
}
-/* Return whether register REGNUM is a real register. */
+/* Return whether register REGNUM is a real (not pseudo) register. */
-static int
+int
real_register (int regnum)
{
return regnum >= 0 && regnum < NUM_REGS;
@@ -109,21 +112,100 @@ real_register (int regnum)
/* Return whether register REGNUM is a pseudo register. */
-static int
+int
pseudo_register (int regnum)
{
return regnum >= NUM_REGS && regnum < NUM_REGS + NUM_PSEUDO_REGS;
}
+/* Return the target number of the first register that "info registers" and
+ "info all-registers" should consider for display. */
+
+int
+register_info_first (void)
+{
+ return 0;
+}
+
+/* Return the target number of the register that "info registers" and "info
+ all-registers" should consider for display after register REGNUM. If no
+ registers should be displayed after register REGNUM, return -1. */
+
+int
+register_info_next (int regnum)
+{
+ regnum++;
+ if (regnum >= NUM_REGS + NUM_PSEUDO_REGS)
+ return -1;
+ if (regnum >= ARCH_NUM_REGS && regnum < NUM_REGS)
+ return NUM_REGS;
+ return regnum;
+}
+
+/* Return whether register REGNUM should be omitted from "info registers"
+ display. */
+
+int
+register_hidesome (int regnum)
+{
+ return TYPE_CODE (REGISTER_VIRTUAL_TYPE (regnum)) == TYPE_CODE_FLT;
+}
+
+/* Return whether register REGNUM should be omitted from "info registers" and
+ "info all-registers" display. */
+
+int
+register_hideall (int regnum)
+{
+ return 0;
+}
+
+/* Return whether register REGNUM is read-only. */
+
+int
+register_rdonly (int regnum)
+{
+ return 0;
+}
+
+/* Return whether side-effects result from reading register REGNUM. */
+
+int
+register_reffect (int regnum)
+{
+ return 0;
+}
+
+/* Return whether register REGNUM is a memory-only register. */
+
+int
+register_memonly (int regnum)
+{
+ return 0;
+}
+
+/* Try to fetch register REGNUM's value from FRAME into RAWBUF. Return 1 on
+ success, 0 on failure if a future attempt might succeed, -1 otherwise. */
+
+int
+fetch_frame_register (struct frame_info *frame, int regnum, char *rawbuf)
+{
+ return !read_relative_register_raw_bytes_for_frame (regnum, rawbuf, frame);
+}
+
/* Fetch register REGNUM into the cache. */
static void
fetch_register (int regnum)
{
- if (real_register (regnum))
- target_fetch_registers (regnum);
- else if (pseudo_register (regnum))
+ if (PSEUDO_REGISTER (regnum))
FETCH_PSEUDO_REGISTER (regnum);
+ else if (REAL_REGISTER (regnum))
+ target_fetch_registers (regnum);
+ else
+ internal_error (__FILE__, __LINE__,
+ "fetch_register: register %d is neither real nor pseudo",
+ regnum);
}
/* Write register REGNUM cached value to the target. */
@@ -131,10 +213,17 @@ fetch_register (int regnum)
static void
store_register (int regnum)
{
- if (real_register (regnum))
- target_store_registers (regnum);
- else if (pseudo_register (regnum))
+ if (REGISTER_RDONLY (regnum))
+ error ("Register %d is read-only.", regnum);
+
+ if (PSEUDO_REGISTER (regnum))
STORE_PSEUDO_REGISTER (regnum);
+ else if (REAL_REGISTER (regnum))
+ target_store_registers (regnum);
+ else
+ internal_error (__FILE__, __LINE__,
+ "store_register: register %d is neither real nor pseudo",
+ regnum);
}
/* FIND_SAVED_REGISTER ()
@@ -296,7 +385,7 @@ get_saved_register (char *raw_buffer,
/* FIXME: This function increases the confusion between FP_REGNUM
and the virtual/pseudo-frame pointer. */
-static int
+int
read_relative_register_raw_bytes_for_frame (int regnum,
char *myaddr,
struct frame_info *frame)
@@ -314,7 +403,7 @@ read_relative_register_raw_bytes_for_fra
get_saved_register (myaddr, &optim, (CORE_ADDR *) NULL, frame,
regnum, (enum lval_type *) NULL);
- if (register_cached (regnum) < 0)
+ if (REGISTER_CACHED (regnum) < 0)
return 1; /* register value not available */
return optim;
@@ -362,12 +451,12 @@ registers_changed (void)
alloca (0);
for (i = 0; i < ARCH_NUM_REGS; i++)
- set_register_cached (i, 0);
+ SET_REGISTER_CACHED (i, 0);
/* Assume that if all the hardware regs have changed,
then so have the pseudo-registers. */
for (i = NUM_REGS; i < NUM_REGS + NUM_PSEUDO_REGS; i++)
- set_register_cached (i, 0);
+ SET_REGISTER_CACHED (i, 0);
if (registers_changed_hook)
registers_changed_hook ();
@@ -384,7 +473,7 @@ registers_fetched (void)
int i;
for (i = 0; i < ARCH_NUM_REGS; i++)
- set_register_cached (i, 1);
+ SET_REGISTER_CACHED (i, 1);
/* Do not assume that the pseudo-regs have also been fetched.
Fetching all real regs might not account for all pseudo-regs. */
}
@@ -429,7 +518,7 @@ read_register_bytes (int inregbyte, char
{
int regstart, regend;
- if (register_cached (regnum))
+ if (REGISTER_CACHED (regnum))
continue;
if (REGISTER_NAME (regnum) == NULL || *REGISTER_NAME (regnum) == '\0')
@@ -446,7 +535,7 @@ read_register_bytes (int inregbyte, char
Update it from the target. */
fetch_register (regnum);
- if (!register_cached (regnum))
+ if (!REGISTER_CACHED (regnum))
{
/* Sometimes pseudoregs are never marked valid, so that they
will be fetched every time (it can be complicated to know
@@ -458,7 +547,7 @@ read_register_bytes (int inregbyte, char
}
if (myaddr != NULL)
- memcpy (myaddr, register_buffer (-1) + inregbyte, inlen);
+ memcpy (myaddr, REGISTER_BUFFER (-1) + inregbyte, inlen);
}
/* Read register REGNUM into memory at MYADDR, which must be large
@@ -475,10 +564,10 @@ read_register_gen (int regnum, char *mya
registers_pid = inferior_pid;
}
- if (!register_cached (regnum))
+ if (!REGISTER_CACHED (regnum))
fetch_register (regnum);
- memcpy (myaddr, register_buffer (regnum),
+ memcpy (myaddr, REGISTER_BUFFER (regnum),
REGISTER_RAW_SIZE (regnum));
}
@@ -511,16 +600,16 @@ write_register_gen (int regnum, char *my
/* If we have a valid copy of the register, and new value == old value,
then don't bother doing the actual store. */
- if (register_cached (regnum)
- && memcmp (register_buffer (regnum), myaddr, size) == 0)
+ if (REGISTER_CACHED (regnum)
+ && memcmp (REGISTER_BUFFER (regnum), myaddr, size) == 0)
return;
- if (real_register (regnum))
+ if (REAL_REGISTER (regnum))
target_prepare_to_store ();
- memcpy (register_buffer (regnum), myaddr, size);
+ memcpy (REGISTER_BUFFER (regnum), myaddr, size);
- set_register_cached (regnum, 1);
+ SET_REGISTER_CACHED (regnum, 1);
store_register (regnum);
}
@@ -568,7 +657,7 @@ write_register_bytes (int myregstart, ch
Update it from the target before scribbling on it. */
read_register_gen (regnum, regbuf);
- memcpy (registers + overlapstart,
+ memcpy (REGISTER_BUFFER (-1) + overlapstart,
myaddr + (overlapstart - myregstart),
overlapend - overlapstart);
@@ -589,10 +678,10 @@ read_register (int regnum)
registers_pid = inferior_pid;
}
- if (!register_cached (regnum))
+ if (!REGISTER_CACHED (regnum))
fetch_register (regnum);
- return (extract_unsigned_integer (register_buffer (regnum),
+ return (extract_unsigned_integer (REGISTER_BUFFER (regnum),
REGISTER_RAW_SIZE (regnum)));
}
@@ -627,10 +716,10 @@ read_signed_register (int regnum)
registers_pid = inferior_pid;
}
- if (!register_cached (regnum))
+ if (!REGISTER_CACHED (regnum))
fetch_register (regnum);
- return (extract_signed_integer (register_buffer (regnum),
+ return (extract_signed_integer (REGISTER_BUFFER (regnum),
REGISTER_RAW_SIZE (regnum)));
}
@@ -680,16 +769,16 @@ write_register (int regnum, LONGEST val)
/* If we have a valid copy of the register, and new value == old value,
then don't bother doing the actual store. */
- if (register_cached (regnum)
- && memcmp (register_buffer (regnum), buf, size) == 0)
+ if (REGISTER_CACHED (regnum)
+ && memcmp (REGISTER_BUFFER (regnum), buf, size) == 0)
return;
- if (real_register (regnum))
+ if (REAL_REGISTER (regnum))
target_prepare_to_store ();
- memcpy (register_buffer (regnum), buf, size);
+ memcpy (REGISTER_BUFFER (regnum), buf, size);
- set_register_cached (regnum, 1);
+ SET_REGISTER_CACHED (regnum, 1);
store_register (regnum);
}
@@ -734,19 +823,19 @@ supply_register (int regnum, char *val)
}
#endif
- set_register_cached (regnum, 1);
+ SET_REGISTER_CACHED (regnum, 1);
if (val)
- memcpy (register_buffer (regnum), val,
+ memcpy (REGISTER_BUFFER (regnum), val,
REGISTER_RAW_SIZE (regnum));
else
- memset (register_buffer (regnum), '\000',
+ memset (REGISTER_BUFFER (regnum), '\000',
REGISTER_RAW_SIZE (regnum));
/* On some architectures, e.g. HPPA, there are a few stray bits in
some registers, that the rest of the code would like to ignore. */
#ifdef CLEAN_UP_REGISTER_VALUE
- CLEAN_UP_REGISTER_VALUE (regnum, register_buffer (regnum));
+ CLEAN_UP_REGISTER_VALUE (regnum, REGISTER_BUFFER (regnum));
#endif
}
@@ -934,10 +1023,14 @@ 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 (REGCACHE_MODULE_ACTIVE)
+ {
+ 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 Tue Feb 13 23:37:16 2001
@@ -0,0 +1,1728 @@
+/* Target register definition interface for GDB, the GNU debugger.
+ Copyright 2001 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"
+
+/* Expand to internal_error()'s required initial arguments. */
+
+#define _ __FILE__, __LINE__,
+
+/* 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 *pseudo_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 */
+ regs_caller_ftype
+ caller_regs; /* callback for locating caller's regs */
+ regs_remap_ftype remap; /* if non-null, callback to change the
+ register memory map */
+ void *remap_data; /* opaque data passed to .remap */
+ };
+
+/* Initialization context returned by regs_init_start(). */
+
+struct regs_init_context
+ {
+ struct gdbarch *gdbarch;
+ struct inf *inf;
+ int virtmax;
+ int max_real_tnum;
+ int npseudos;
+ int regssz;
+ };
+
+/* Memory region search key used by find_mem(). */
+
+struct memfind
+ {
+ CORE_ADDR addr; /* start of memory region */
+ int len; /* length of region */
+ };
+
+/* Identifier returned by register_gdbarch_data(). */
+
+static struct gdbarch_data *inf_id;
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* Update the register memory map. */
+
+static void
+update_mem (struct inf *inf)
+{
+ if (inf->remap && inf->remap (inf->remap_data))
+ qsort (inf->mems, inf->nmregs, sizeof (struct reg *), init_mem_cmp);
+}
+
+/* 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 = gdbarch_data (inf_id);
+ 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 = gdbarch_data (inf_id);
+ if (!inf->nmregs)
+ return NULL;
+
+ update_mem (inf);
+
+ 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->pseudo_data,
+ rawbuf);
+
+ val->type = int_valtype (valid, val_pseudo);
+ memcpy (val->rawbuf, rawbuf, reg->size);
+}
+
+/* Try to set 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->pseudo_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 = gdbarch_data (inf_id);
+ 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 FRAME'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 = gdbarch_data (inf_id);
+ 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 ®->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
+ {
+ /* Will return nonlocally if there were problems. */
+ target_store_registers (reg->tnum);
+ val->type = val_real;
+ }
+
+ 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 = gdbarch_data (inf_id);
+ 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;
+ }
+}
+
+/* Set each memory-mapped register's memory location to ADJUST (<regnum>,
+ <mem>, DATA) where <regnum> and <mem> are the register's target number and
+ current memory location, respectively. */
+
+void
+regs_remap (CORE_ADDR (*adjust) (int, CORE_ADDR, void *), void *data)
+{
+ struct inf *inf;
+ struct reg *reg;
+ int i;
+
+ inf = gdbarch_data (inf_id);
+
+ for (i = 0; i < inf->nmregs; i++)
+ {
+ reg = inf->mems[i];
+ reg->mem = adjust (reg->tnum, reg->mem, data);
+ }
+}
+
+/* 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 memory register. */
+
+int
+regs_rpseudo_mem (struct frame_info *frame, int tnum, int *parents,
+ void *data, char *rawbuf)
+{
+ struct reg *reg;
+
+ reg = find_tnum (tnum, "regs_rpseudo_mem");
+ update_mem (gdbarch_data (inf_id));
+ return !target_read_memory (reg->mem, rawbuf, reg->size);
+}
+
+/* Pseudo-register callback to write an alias register. */
+
+int
+regs_wpseudo_mem (struct frame_info *frame, int tnum, int *parents, void
+ *data, char *rawbuf)
+{
+ struct reg *reg;
+
+ reg = find_tnum (tnum, "regs_store_memory_register");
+ update_mem (gdbarch_data (inf_id));
+ return !target_write_memory (reg->mem, rawbuf, reg->size);
+}
+
+/* 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 = gdbarch_data (inf_id);
+
+ 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 = gdbarch_data (inf_id);
+ 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:
+ case val_pseudo:
+ 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 = gdbarch_data (inf_id);
+ 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 = gdbarch_data (inf_id);
+ reg = find_tnum (tnum, "regs_fetch_pseudo_register");
+
+ if (reg->val.type == val_fetch)
+ fetch_pseudo (NULL, 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 = gdbarch_data (inf_id);
+ reg = find_tnum (tnum, "regs_store_pseudo_register");
+
+ store_pseudo (NULL, 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 the register with LEN-byte NAME, or -1 if no
+ such register exists. */
+
+static int
+regs_register_name_tnum (char *name, int len)
+{
+ struct inf *inf;
+ struct reg *reg;
+
+ inf = gdbarch_data (inf_id);
+ 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 >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. */
+
+static int
+regs_register_cached (int tnum)
+{
+ struct reg *reg;
+
+ reg = find_tnum (tnum, "regs_register_cached");
+ 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. */
+
+static void
+regs_set_register_cached (int tnum, int state)
+{
+ struct reg *reg;
+
+ reg = find_tnum (tnum, "regs_set_register_cached");
+ 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. */
+
+static char *
+regs_register_buffer (int tnum)
+{
+ struct reg *reg;
+ struct inf *inf;
+
+ inf = gdbarch_data (inf_id);
+ if (tnum < 0)
+ return inf->cache;
+ else
+ {
+ reg = find_tnum (tnum, "regs_register_buffer");
+ return reg->val.rawbuf;
+ }
+}
+
+/* Return whether register TNUM is a real (not pseudo) register. */
+
+static int
+regs_real_register (int tnum)
+{
+ struct reg *reg;
+
+ reg = find_tnum (tnum, NULL);
+ return reg && reg->flags & REGS_REAL;
+}
+
+/* Return whether register TNUM is a pseudo register. */
+
+static int
+regs_pseudo_register (int tnum)
+{
+ struct reg *reg;
+
+ reg = find_tnum (tnum, NULL);
+ return reg && reg->flags & REGS_PSEUDO;
+}
+
+/* Return the target number of the first register that "info registers" and
+ "info all-registers" should consider for display. */
+
+static int
+regs_register_info_first (void)
+{
+ struct inf *inf;
+
+ inf = gdbarch_data (inf_id);
+ if (!inf->nregs)
+ return -1;
+ return inf->regs[0].tnum;
+}
+
+/* Return the target number of the register that "info registers" and "info
+ all-registers" should consider for display after register TNUM. If no
+ registers should be displayed after register TNUM, return -1. */
+
+static int
+regs_register_info_next (int tnum)
+{
+ struct inf *inf;
+ struct reg *reg;
+
+ inf = gdbarch_data (inf_id);
+ reg = find_tnum (tnum, "regs_register_info_next");
+ if (++reg - inf->regs == inf->nregs)
+ return -1;
+ return reg->tnum;
+}
+
+/* Return the target number of the register mapped to memory ADDR, or -1 if no
+ such register exists. */
+
+static int
+regs_register_addr_tnum (CORE_ADDR addr)
+{
+ struct reg *reg, **found;
+ struct memfind mem;
+
+ found = find_mem (addr, 1);
+ if (!found)
+ return -1;
+
+ reg = *found;
+ if (reg->mem != addr)
+ return -1;
+
+ return reg->tnum;
+}
+
+/* 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. */
+
+static void
+regs_register_write_memory (CORE_ADDR addr, int len)
+{
+ struct reg *reg, **found;
+ struct memfind mem;
+ struct inf *inf;
+
+ inf = gdbarch_data (inf_id);
+ 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. */
+
+ for (; found > inf->mems; found--)
+ {
+ reg = found[-1];
+ if (reg->mem + reg->size <= addr)
+ break;
+ }
+
+ for (; found < inf->mems + inf->nmregs; found++)
+ {
+ reg = *found;
+ if (reg->mem >= addr + len)
+ break;
+ set_valid (reg, 0);
+ }
+}
+
+/* Return whether register TNUM's FLAGS is set. */
+
+static int
+regs_register_flag (int tnum, int flag)
+{
+ struct reg *reg;
+
+ reg = find_tnum (tnum, "regs_register_flag");
+ return reg->flags & flag;
+}
+
+/* Return whether register TNUM should be omitted from "info registers"
+ display. */
+
+static int
+regs_register_hidesome (int tnum)
+{
+ return regs_register_flag (tnum, REGS_HIDESOME);
+}
+
+/* Return whether register TNUM should be omitted from "info registers" and
+ "info all-registers" display. */
+
+static int
+regs_register_hideall (int tnum)
+{
+ return regs_register_flag (tnum, REGS_HIDEALL);
+}
+
+/* Return whether register TNUM is read-only. */
+
+static int
+regs_register_rdonly (int tnum)
+{
+ return regs_register_flag (tnum, REGS_RDONLY);
+}
+
+/* Return whether side-effects result from reading register TNUM. */
+
+static int
+regs_register_reffect (int tnum)
+{
+ return regs_register_flag (tnum, REGS_REFFECT);
+}
+
+/* Initialize INF's register lookup mechanisms. */
+
+static void
+init_lookups (struct inf *inf)
+{
+ struct reg *reg;
+ int i, j, k;
+
+ inf->tnums = xmalloc (sizeof (struct reg *) * inf->nregs);
+
+ if (inf->nnregs)
+ inf->names = xmalloc (sizeof (struct reg *) * inf->nnregs);
+ else
+ inf->names = NULL;
+
+ if (inf->nmregs)
+ inf->mems = 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 = xmalloc (inf->namemax + 1);
+}
+
+/* Initialize INF's cache storage. */
+
+static void
+init_cache (struct inf *inf)
+{
+ int i, offset;
+ struct reg *reg;
+
+ inf->cache = 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->max_real_tnum + ++context->npseudos;
+ }
+}
+
+/* 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 = xmalloc (sizeof (int));
+
+ /* Allocate space for counting each register's children. */
+ ccounts = 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 = 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 = xmalloc (2 * sizeof (struct reg *));
+ else
+ reg2->children = 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. Architectures
+ should call this function, individual register definition functions like
+ regs_init_real(), and regs_init_finish() from their gdbarch_register()
+ callbacks.
+
+ 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. */
+
+struct regs_init_context *
+regs_init_start (struct gdbarch *gdbarch, regs_caller_ftype caller_regs)
+{
+ struct regs_init_context *context;
+ struct inf *inf;
+
+ context = xmalloc (sizeof *context);
+ context->gdbarch = gdbarch;
+ context->inf = inf = xmalloc (sizeof (struct inf));
+
+ inf->caller_regs = caller_regs;
+ inf->remap = NULL;
+ 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->max_real_tnum = -1;
+ context->npseudos = 0;
+ 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 *pseudo_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 = xmalloc (sizeof (struct reg));
+ }
+ else if (context->regssz == inf->nregs)
+ {
+ context->regssz *= 2;
+ inf->regs = 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->pseudo_data = pseudo_data;
+
+ if (!parents)
+ reg->parents = NULL;
+ else
+ {
+ for (i = 0; parents[i] >= 0; i++)
+ ;
+ reg->parents = 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->max_real_tnum)
+ context->max_real_tnum = 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, CORE_ADDR mem, unsigned int flags,
+ regs_rpseudo_ftype rpseudo, regs_wpseudo_ftype wpseudo,
+ void *data, int *parents)
+{
+ init_any (context, name, 0, 0, size, type, mem, flags,
+ rpseudo, wpseudo, data, parents);
+}
+
+/* Allocate and initialize a copy of REMAP_DATA, and pass the address of that
+ copy to REMAP() whenever the register memory map is checked.
+
+ REMAP() can change the map by calling regs_remap(). It returns 1 if it
+ changed the map and 0 otherwise. */
+
+void
+regs_init_remap (struct regs_init_context *context, regs_remap_ftype remap,
+ void *data)
+{
+ struct inf *inf;
+
+ inf = context->inf;
+ inf->remap = remap;
+ inf->remap_data = data;
+}
+
+/* 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 = xrealloc (inf->regs, inf->nregs * sizeof (struct reg));
+
+ init_pseudo (inf, context);
+ init_lookups (inf);
+ init_cache (inf);
+ init_children (inf);
+
+ /* Set gdbarch fields. */
+ set_gdbarch_num_regs (gdbarch, context->max_real_tnum + 1);
+ set_gdbarch_num_pseudo_regs (gdbarch, context->npseudos);
+ 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_register_name_tnum (gdbarch, regs_register_name_tnum);
+ set_gdbarch_register_cached (gdbarch, regs_register_cached);
+ set_gdbarch_set_register_cached (gdbarch, regs_set_register_cached);
+ set_gdbarch_register_buffer (gdbarch, regs_register_buffer);
+ set_gdbarch_real_register (gdbarch, regs_real_register);
+ set_gdbarch_pseudo_register (gdbarch, regs_pseudo_register);
+ set_gdbarch_register_info_first (gdbarch, regs_register_info_first);
+ set_gdbarch_register_info_next (gdbarch, regs_register_info_next);
+ set_gdbarch_register_addr_tnum (gdbarch, regs_register_addr_tnum);
+ set_gdbarch_register_write_memory (gdbarch, regs_register_write_memory);
+ set_gdbarch_register_hidesome (gdbarch, regs_register_hidesome);
+ set_gdbarch_register_hideall (gdbarch, regs_register_hideall);
+ set_gdbarch_register_rdonly (gdbarch, regs_register_rdonly);
+ set_gdbarch_register_reffect (gdbarch, regs_register_reffect);
+ set_gdbarch_fetch_frame_register (gdbarch, regs_fetch_frame);
+ 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_regcache_module_active (gdbarch, 0);
+ set_gdbarch_data (gdbarch, inf_id, inf);
+
+ /* 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);
+}
+
+/* Module initialization. */
+
+void
+_initialize_regs (void)
+{
+ inf_id = register_gdbarch_data (NULL, NULL);
+}
Index: gdb/regs.h
===================================================================
diff -up /dev/null gdb/regs.h
--- /dev/null Sun Feb 12 03:29:56 1995
+++ gdb/regs.h Tue Feb 13 23:09:38 2001
@@ -0,0 +1,268 @@
+/* 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
+
+/* 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_RDONLY = 0x04, /* register is read-only */
+ REGS_RTHRU = 0x08, /* don't cache reads */
+ REGS_WTHRU = 0x10, /* don't cache writes (not implemented) */
+ REGS_REFFECT = 0x20 /* reads have side-effects, so don't display
+ during "info [all-]registers" unless
+ explicitly requested */
+ };
+
+/* 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 the callback will never be passed
+ a dummy frame argument. Otherwise, the callback is responsible for
+ noticing dummy frames and copying saved dummy caller register state. */
+
+typedef void (*regs_caller_ftype) (struct frame_info *frame);
+
+/* Optional callback to update the register memory map. If the map needs
+ updating, the callback should update it using regs_remap() and return 1,
+ else it should return 0.
+
+ DATA points to per-architecture storage allocated by the register module
+ and initialized by regs_init_remap(). */
+
+typedef int (*regs_remap_ftype) (void **data);
+
+/* Pseudo-register read and write callback types. Read or write
+ pseudo-register REGNUM in FRAME to or from RAWBUF. PARENTS if non-null is
+ a -1 terminated list of real registers on which the pseudo-register
+ depends.
+
+ 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);
+
+/* 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 mem, unsigned int flags);
+
+/* Define a pseudo-register. */
+
+extern void regs_init_pseudo (struct regs_init_context *context, char *name,
+ int size, struct type **type, CORE_ADDR mem,
+ unsigned int flags, regs_rpseudo_ftype rpseudo,
+ regs_wpseudo_ftype wpseudo, void *data,
+ int *parents);
+
+/* Specify a register remap callback. */
+
+extern void regs_init_remap (struct regs_init_context *context,
+ regs_remap_ftype remap, void *data);
+
+/* 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);
+
+/* Set each memory-mapped register's memory location to ADJUST (<regnum>,
+ <mem>, DATA) where <regnum> and <mem> are the register's target number and
+ current memory location, respectively. */
+
+extern void regs_remap (CORE_ADDR (*adjust) (int, CORE_ADDR, void *),
+ void *data);
+
+/* 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 memory registers. */
+
+extern int regs_rpseudo_mem (struct frame_info *frame, int tnum,
+ int *parents, void *data, char *rawbuf);
+extern int regs_wpseudo_mem (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);
+
+#endif /* !GDB_REGS_H */
Index: gdb/remote.c
===================================================================
diff -up gdb/remote.c gdb/remote.c
--- gdb/remote.c Thu Feb 15 00:53:07 2001
+++ gdb/remote.c Tue Feb 13 23:09:38 2001
@@ -3092,7 +3092,7 @@ supply_them:
{
supply_register (i, ®s[REGISTER_BYTE (i)]);
if (buf[REGISTER_BYTE (i) * 2] == 'x')
- set_register_cached (i, -1);
+ SET_REGISTER_CACHED (i, -1);
}
}
@@ -3129,7 +3129,7 @@ store_register_using_P (int regno)
sprintf (buf, "P%x=", regno);
p = buf + strlen (buf);
- regp = register_buffer (regno);
+ regp = REGISTER_BUFFER (regno);
for (i = 0; i < REGISTER_RAW_SIZE (regno); ++i)
{
*p++ = tohex ((regp[i] >> 4) & 0xf);
@@ -3189,7 +3189,7 @@ remote_store_registers (int regno)
/* Command describes registers byte by byte,
each byte encoded as two hex characters. */
- regs = register_buffer (-1);
+ regs = REGISTER_BUFFER (-1);
p = buf + 1;
/* remote_prepare_to_store insures that register_bytes_found gets set. */
for (i = 0; i < register_bytes_found; i++)
Index: gdb/target.c
===================================================================
diff -up gdb/target.c gdb/target.c
--- gdb/target.c Thu Feb 15 00:53:19 2001
+++ gdb/target.c Tue Feb 13 23:09:38 2001
@@ -856,6 +856,10 @@ do_xfer_memory (CORE_ADDR memaddr, char
if (len == 0)
return 0;
+ /* Deal with memory-mapped registers. */
+ if (write && REGISTER_WRITE_MEMORY_P ())
+ REGISTER_WRITE_MEMORY (memaddr, len);
+
/* to_xfer_memory is not guaranteed to set errno, even when it returns
0. */
errno = 0;
Index: gdb/value.h
===================================================================
diff -up gdb/value.h gdb/value.h
--- gdb/value.h Thu Feb 15 00:53:26 2001
+++ gdb/value.h Tue Feb 13 23:09:38 2001
@@ -499,10 +499,36 @@ extern void register_changed (int regnum
extern char *register_buffer (int regnum);
+extern int real_register (int regnum);
+
+extern int pseudo_register (int regnum);
+
+extern int register_info_first (void);
+
+extern int register_info_next (int regnum);
+
+extern int register_hidesome (int regnum);
+
+extern int register_hideall (int regnum);
+
+extern int register_rdonly (int regnum);
+
+extern int register_reffect (int regnum);
+
+extern int register_memonly (int regnum);
+
+extern int fetch_frame_register (struct frame_info *frame, int regnum,
+ char *rawbuf);
+
extern void get_saved_register (char *raw_buffer, int *optimized,
CORE_ADDR * addrp,
struct frame_info *frame,
int regnum, enum lval_type *lval);
+
+extern int read_relative_register_raw_bytes (int regnum, char *myaddr);
+
+extern int read_relative_register_raw_bytes_for_frame
+ (int regnum, char *myaddr, struct frame_info *frame);
extern void
modify_field (char *addr, LONGEST fieldval, int bitpos, int bitsize);