From mboxrd@z Thu Jan 1 00:00:00 1970 From: Nick Duffek To: ac131313@cygnus.com Cc: gdb@sources.redhat.com Subject: Re: A better register interface Date: Wed, 14 Feb 2001 22:17:00 -0000 Message-id: <200102150624.f1F6O5F14315@rtl.cygnus.com> References: <3A8B5C60.92EEABA3@cygnus.com> X-SW-Source: 2001-02/msg00169.html 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 "" +#define DISP_NOVAL "" +#define DISP_EFFECT "" + +/* 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 )". */ + 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 + +#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 in LEN-element ARRAY for + which CMP (KEY, ) 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 (, + , DATA) where and 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 (, + , DATA) where and 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);