2002-08-19 Elena Zannoni <ezannoni@redhat.com>
[deliverable/binutils-gdb.git] / gdb / regcache.c
index 592b3e59c15837609c8b842605232beb6208d0c1..e483de08cfd72ad5fff83192e0a68523a23face7 100644 (file)
@@ -27,6 +27,8 @@
 #include "gdbcmd.h"
 #include "regcache.h"
 #include "gdb_assert.h"
+#include "gdb_string.h"
+#include "gdbcmd.h"            /* For maintenanceprintlist.  */
 
 /*
  * DATA STRUCTURE
@@ -143,8 +145,8 @@ init_regcache_descr (struct gdbarch *gdbarch)
 
   /* If an old style architecture, construct the register cache
      description using all the register macros.  */
-  if (!gdbarch_register_read_p (gdbarch)
-      && !gdbarch_register_write_p (gdbarch))
+  if (!gdbarch_pseudo_register_read_p (gdbarch)
+      && !gdbarch_pseudo_register_write_p (gdbarch))
     return init_legacy_regcache_descr (gdbarch);
 
   descr = XMALLOC (struct regcache_descr);
@@ -159,7 +161,12 @@ init_regcache_descr (struct gdbarch *gdbarch)
   /* Construct a strictly RAW register cache.  Don't allow pseudo's
      into the register cache.  */
   descr->nr_raw_registers = NUM_REGS;
-  descr->sizeof_raw_register_valid_p = NUM_REGS;
+
+  /* FIXME: cagney/2002-08-13: Overallocate the register_valid_p
+     array.  This pretects GDB from erant code that accesses elements
+     of the global register_valid_p[] array in the range [NUM_REGS
+     .. NUM_REGS + NUM_PSEUDO_REGS).  */
+  descr->sizeof_raw_register_valid_p = NUM_REGS + NUM_PSEUDO_REGS;
 
   /* Lay out the register cache.  The pseud-registers are included in
      the layout even though their value isn't stored in the register
@@ -364,17 +371,6 @@ regcache_valid_p (struct regcache *regcache, int regnum)
   return regcache->raw_register_valid_p[regnum];
 }
 
-CORE_ADDR
-regcache_raw_read_as_address (struct regcache *regcache, int regnum)
-{
-  char *buf;
-  gdb_assert (regcache != NULL);
-  gdb_assert (regnum >= 0 && regnum < regcache->descr->nr_raw_registers);
-  buf = alloca (regcache->descr->sizeof_register[regnum]);
-  regcache_raw_read (regcache, regnum, buf);
-  return extract_address (buf, regcache->descr->sizeof_register[regnum]);
-}
-
 char *
 deprecated_grub_regcache_for_registers (struct regcache *regcache)
 {
@@ -440,7 +436,9 @@ register_cached (int regnum)
 void
 set_register_cached (int regnum, int state)
 {
-  register_valid[regnum] = state;
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < current_regcache->descr->nr_raw_registers);
+  current_regcache->raw_register_valid_p[regnum] = state;
 }
 
 /* REGISTER_CHANGED
@@ -469,46 +467,6 @@ real_register (int regnum)
   return regnum >= 0 && regnum < NUM_REGS;
 }
 
-/* Return whether register REGNUM is a pseudo register.  */
-
-static int
-pseudo_register (int regnum)
-{
-  return regnum >= NUM_REGS && regnum < NUM_REGS + NUM_PSEUDO_REGS;
-}
-
-/* Fetch register REGNUM into the cache.  */
-
-static void
-fetch_register (int regnum)
-{
-  /* NOTE: cagney/2001-12-04: Legacy targets were using fetch/store
-     pseudo-register as a way of handling registers that needed to be
-     constructed from one or more raw registers.  New targets instead
-     use gdbarch register read/write.  */
-  if (FETCH_PSEUDO_REGISTER_P ()
-      && pseudo_register (regnum))
-    FETCH_PSEUDO_REGISTER (regnum);
-  else
-    target_fetch_registers (regnum);
-}
-
-/* Write register REGNUM cached value to the target.  */
-
-static void
-store_register (int regnum)
-{
-  /* NOTE: cagney/2001-12-04: Legacy targets were using fetch/store
-     pseudo-register as a way of handling registers that needed to be
-     constructed from one or more raw registers.  New targets instead
-     use gdbarch register read/write.  */
-  if (STORE_PSEUDO_REGISTER_P ()
-      && pseudo_register (regnum))
-    STORE_PSEUDO_REGISTER (regnum);
-  else
-    target_store_registers (regnum);
-}
-
 /* Low level examining and depositing of registers.
 
    The caller is responsible for making sure that the inferior is
@@ -534,7 +492,7 @@ registers_changed (void)
      gdb gives control to the user (ie watchpoints).  */
   alloca (0);
 
-  for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS; i++)
+  for (i = 0; i < current_regcache->descr->nr_raw_registers; i++)
     set_register_cached (i, 0);
 
   if (registers_changed_hook)
@@ -667,7 +625,7 @@ legacy_read_register_gen (int regnum, char *myaddr)
     }
 
   if (!register_cached (regnum))
-    fetch_register (regnum);
+    target_fetch_registers (regnum);
 
   memcpy (myaddr, register_buffer (current_regcache, regnum),
          REGISTER_RAW_SIZE (regnum));
@@ -701,7 +659,7 @@ regcache_raw_read (struct regcache *regcache, int regnum, void *buf)
          registers_ptid = inferior_ptid;
        }
       if (!register_cached (regnum))
-       fetch_register (regnum);
+       target_fetch_registers (regnum);
     }
   /* Copy the value directly into the register cache.  */
   memcpy (buf, (regcache->raw_registers
@@ -709,6 +667,31 @@ regcache_raw_read (struct regcache *regcache, int regnum, void *buf)
          regcache->descr->sizeof_register[regnum]);
 }
 
+void
+regcache_raw_read_signed (struct regcache *regcache, int regnum, LONGEST *val)
+{
+  char *buf;
+  gdb_assert (regcache != NULL);
+  gdb_assert (regnum >= 0 && regnum < regcache->descr->nr_raw_registers);
+  buf = alloca (regcache->descr->sizeof_register[regnum]);
+  regcache_raw_read (regcache, regnum, buf);
+  (*val) = extract_signed_integer (buf,
+                                  regcache->descr->sizeof_register[regnum]);
+}
+
+void
+regcache_raw_read_unsigned (struct regcache *regcache, int regnum,
+                           ULONGEST *val)
+{
+  char *buf;
+  gdb_assert (regcache != NULL);
+  gdb_assert (regnum >= 0 && regnum < regcache->descr->nr_raw_registers);
+  buf = alloca (regcache->descr->sizeof_register[regnum]);
+  regcache_raw_read (regcache, regnum, buf);
+  (*val) = extract_unsigned_integer (buf,
+                                    regcache->descr->sizeof_register[regnum]);
+}
+
 void
 read_register_gen (int regnum, char *buf)
 {
@@ -719,14 +702,46 @@ read_register_gen (int regnum, char *buf)
       legacy_read_register_gen (regnum, buf);
       return;
     }
+  regcache_cooked_read (current_regcache, regnum, buf);
+}
+
+void
+regcache_cooked_read (struct regcache *regcache, int regnum, void *buf)
+{
   gdb_assert (regnum >= 0);
-  gdb_assert (regnum < current_regcache->descr->nr_cooked_registers);
-  if (regnum < current_regcache->descr->nr_raw_registers)
-    regcache_raw_read (current_regcache, regnum, buf);
+  gdb_assert (regnum < regcache->descr->nr_cooked_registers);
+  if (regnum < regcache->descr->nr_raw_registers)
+    regcache_raw_read (regcache, regnum, buf);
   else
-    gdbarch_register_read (current_gdbarch, regnum, buf);
+    gdbarch_pseudo_register_read (regcache->descr->gdbarch, regcache,
+                                 regnum, buf);
 }
 
+void
+regcache_cooked_read_signed (struct regcache *regcache, int regnum,
+                            LONGEST *val)
+{
+  char *buf;
+  gdb_assert (regcache != NULL);
+  gdb_assert (regnum >= 0 && regnum < regcache->descr->nr_raw_registers);
+  buf = alloca (regcache->descr->sizeof_register[regnum]);
+  regcache_cooked_read (regcache, regnum, buf);
+  (*val) = extract_signed_integer (buf,
+                                  regcache->descr->sizeof_register[regnum]);
+}
+
+void
+regcache_cooked_read_unsigned (struct regcache *regcache, int regnum,
+                              ULONGEST *val)
+{
+  char *buf;
+  gdb_assert (regcache != NULL);
+  gdb_assert (regnum >= 0 && regnum < regcache->descr->nr_raw_registers);
+  buf = alloca (regcache->descr->sizeof_register[regnum]);
+  regcache_cooked_read (regcache, regnum, buf);
+  (*val) = extract_unsigned_integer (buf,
+                                    regcache->descr->sizeof_register[regnum]);
+}
 
 /* Write register REGNUM at MYADDR to the target.  MYADDR points at
    REGISTER_RAW_BYTES(REGNUM), which must be in target byte-order.  */
@@ -765,7 +780,7 @@ legacy_write_register_gen (int regnum, const void *myaddr)
   memcpy (register_buffer (current_regcache, regnum), myaddr, size);
 
   set_register_cached (regnum, 1);
-  store_register (regnum);
+  target_store_registers (regnum);
 }
 
 void
@@ -820,7 +835,7 @@ regcache_raw_write (struct regcache *regcache, int regnum, const void *buf)
   memcpy (register_buffer (regcache, regnum), buf,
          regcache->descr->sizeof_register[regnum]);
   regcache->raw_register_valid_p[regnum] = 1;
-  store_register (regnum);
+  target_store_registers (regnum);
 }
 
 void
@@ -833,12 +848,19 @@ write_register_gen (int regnum, char *buf)
       legacy_write_register_gen (regnum, buf);
       return;
     }
+  regcache_cooked_write (current_regcache, regnum, buf);
+}
+
+void
+regcache_cooked_write (struct regcache *regcache, int regnum, const void *buf)
+{
   gdb_assert (regnum >= 0);
-  gdb_assert (regnum < current_regcache->descr->nr_cooked_registers);
-  if (regnum < current_regcache->descr->nr_raw_registers)
-    regcache_raw_write (current_regcache, regnum, buf);
+  gdb_assert (regnum < regcache->descr->nr_cooked_registers);
+  if (regnum < regcache->descr->nr_raw_registers)
+    regcache_raw_write (regcache, regnum, buf);
   else
-    gdbarch_register_write (current_gdbarch, regnum, buf);
+    gdbarch_pseudo_register_write (regcache->descr->gdbarch, regcache,
+                                  regnum, buf);
 }
 
 /* Copy INLEN bytes of consecutive data from memory at MYADDR
@@ -889,11 +911,91 @@ write_register_bytes (int myregstart, char *myaddr, int inlen)
                  myaddr + (overlapstart - myregstart),
                  overlapend - overlapstart);
 
-         store_register (regnum);
+         target_store_registers (regnum);
        }
     }
 }
 
+/* Perform a partial register transfer using a read, modify, write
+   operation.  */
+
+typedef void (regcache_read_ftype) (struct regcache *regcache, int regnum,
+                                   void *buf);
+typedef void (regcache_write_ftype) (struct regcache *regcache, int regnum,
+                                    const void *buf);
+
+void
+regcache_xfer_part (struct regcache *regcache, int regnum,
+                   int offset, int len, void *in, const void *out,
+                   regcache_read_ftype *read, regcache_write_ftype *write)
+{
+  struct regcache_descr *descr = regcache->descr;
+  bfd_byte *reg = alloca (descr->max_register_size);
+  gdb_assert (offset >= 0 && offset <= descr->sizeof_register[regnum]);
+  gdb_assert (len >= 0 && offset + len <= descr->sizeof_register[regnum]);
+  /* Something to do?  */
+  if (offset + len == 0)
+    return;
+  /* Read (when needed) ... */
+  if (in != NULL
+      || offset > 0
+      || offset + len < descr->sizeof_register[regnum])
+    {
+      gdb_assert (read != NULL);
+      read (regcache, regnum, reg);
+    }
+  /* ... modify ... */
+  if (in != NULL)
+    memcpy (in, reg + offset, len);
+  if (out != NULL)
+    memcpy (reg + offset, out, len);
+  /* ... write (when needed).  */
+  if (out != NULL)
+    {
+      gdb_assert (write != NULL);
+      write (regcache, regnum, reg);
+    }
+}
+
+void
+regcache_raw_read_part (struct regcache *regcache, int regnum,
+                       int offset, int len, void *buf)
+{
+  struct regcache_descr *descr = regcache->descr;
+  gdb_assert (regnum >= 0 && regnum < descr->nr_raw_registers);
+  regcache_xfer_part (regcache, regnum, offset, len, buf, NULL,
+                     regcache_raw_read, regcache_raw_write);
+}
+
+void
+regcache_raw_write_part (struct regcache *regcache, int regnum,
+                        int offset, int len, const void *buf)
+{
+  struct regcache_descr *descr = regcache->descr;
+  gdb_assert (regnum >= 0 && regnum < descr->nr_raw_registers);
+  regcache_xfer_part (regcache, regnum, offset, len, NULL, buf,
+                     regcache_raw_read, regcache_raw_write);
+}
+
+void
+regcache_cooked_read_part (struct regcache *regcache, int regnum,
+                          int offset, int len, void *buf)
+{
+  struct regcache_descr *descr = regcache->descr;
+  gdb_assert (regnum >= 0 && regnum < descr->nr_cooked_registers);
+  regcache_xfer_part (regcache, regnum, offset, len, buf, NULL,
+                     regcache_cooked_read, regcache_cooked_write);
+}
+
+void
+regcache_cooked_write_part (struct regcache *regcache, int regnum,
+                           int offset, int len, const void *buf)
+{
+  struct regcache_descr *descr = regcache->descr;
+  gdb_assert (regnum >= 0 && regnum < descr->nr_cooked_registers);
+  regcache_xfer_part (regcache, regnum, offset, len, NULL, buf,
+                     regcache_cooked_read, regcache_cooked_write);
+}
 
 /* Return the contents of register REGNUM as an unsigned integer.  */
 
@@ -1024,7 +1126,7 @@ supply_register (int regnum, const void *val)
   /* NOTE: cagney/2001-03-16: The macro CLEAN_UP_REGISTER_VALUE is
      going to be deprecated.  Instead architectures will leave the raw
      register value as is and instead clean things up as they pass
-     through the method gdbarch_register_read() clean up the
+     through the method gdbarch_pseudo_register_read() clean up the
      values. */
 
 #ifdef DEPRECATED_CLEAN_UP_REGISTER_VALUE
@@ -1211,6 +1313,238 @@ build_regcache (void)
   register_valid = deprecated_grub_regcache_for_register_valid (current_regcache);
 }
 
+static void
+dump_endian_bytes (struct ui_file *file, enum bfd_endian endian,
+                  const unsigned char *buf, long len)
+{
+  int i;
+  switch (endian)
+    {
+    case BFD_ENDIAN_BIG:
+      for (i = 0; i < len; i++)
+       fprintf_unfiltered (file, "%02x", buf[i]);
+      break;
+    case BFD_ENDIAN_LITTLE:
+      for (i = len - 1; i >= 0; i--)
+       fprintf_unfiltered (file, "%02x", buf[i]);
+      break;
+    default:
+      internal_error (__FILE__, __LINE__, "Bad switch");
+    }
+}
+
+enum regcache_dump_what
+{
+  regcache_dump_none, regcache_dump_raw, regcache_dump_cooked
+};
+
+static void
+regcache_dump (struct regcache *regcache, struct ui_file *file,
+              enum regcache_dump_what what_to_dump)
+{
+  struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
+  int regnum;
+  int footnote_nr = 0;
+  int footnote_register_size = 0;
+  int footnote_register_offset = 0;
+  int footnote_register_type_name_null = 0;
+  long register_offset = 0;
+  unsigned char *buf = alloca (regcache->descr->max_register_size);
+
+#if 0
+  fprintf_unfiltered (file, "legacy_p %d\n", regcache->descr->legacy_p);
+  fprintf_unfiltered (file, "nr_raw_registers %d\n",
+                     regcache->descr->nr_raw_registers);
+  fprintf_unfiltered (file, "nr_cooked_registers %d\n",
+                     regcache->descr->nr_cooked_registers);
+  fprintf_unfiltered (file, "sizeof_raw_registers %ld\n",
+                     regcache->descr->sizeof_raw_registers);
+  fprintf_unfiltered (file, "sizeof_raw_register_valid_p %ld\n",
+                     regcache->descr->sizeof_raw_register_valid_p);
+  fprintf_unfiltered (file, "max_register_size %ld\n",
+                     regcache->descr->max_register_size);
+  fprintf_unfiltered (file, "NUM_REGS %d\n", NUM_REGS);
+  fprintf_unfiltered (file, "NUM_PSEUDO_REGS %d\n", NUM_PSEUDO_REGS);
+#endif
+
+  gdb_assert (regcache->descr->nr_cooked_registers
+             == (NUM_REGS + NUM_PSEUDO_REGS));
+
+  for (regnum = -1; regnum < regcache->descr->nr_cooked_registers; regnum++)
+    {
+      /* Name.  */
+      if (regnum < 0)
+       fprintf_unfiltered (file, " %-10s", "Name");
+      else
+       {
+         const char *p = REGISTER_NAME (regnum);
+         if (p == NULL)
+           p = "";
+         else if (p[0] == '\0')
+           p = "''";
+         fprintf_unfiltered (file, " %-10s", p);
+       }
+
+      /* Number.  */
+      if (regnum < 0)
+       fprintf_unfiltered (file, " %4s", "Nr");
+      else
+       fprintf_unfiltered (file, " %4d", regnum);
+
+      /* Relative number.  */
+      if (regnum < 0)
+       fprintf_unfiltered (file, " %4s", "Rel");
+      else if (regnum < NUM_REGS)
+       fprintf_unfiltered (file, " %4d", regnum);
+      else
+       fprintf_unfiltered (file, " %4d", (regnum - NUM_REGS));
+
+      /* Offset.  */
+      if (regnum < 0)
+       fprintf_unfiltered (file, " %6s  ", "Offset");
+      else
+       {
+         fprintf_unfiltered (file, " %6ld",
+                             regcache->descr->register_offset[regnum]);
+         if (register_offset != regcache->descr->register_offset[regnum]
+             || register_offset != REGISTER_BYTE (regnum))
+           {
+             if (!footnote_register_offset)
+               footnote_register_offset = ++footnote_nr;
+             fprintf_unfiltered (file, "*%d", footnote_register_offset);
+           }
+         else
+           fprintf_unfiltered (file, "  ");
+         register_offset = (regcache->descr->register_offset[regnum]
+                            + regcache->descr->sizeof_register[regnum]);
+       }
+
+      /* Size.  */
+      if (regnum < 0)
+       fprintf_unfiltered (file, " %5s ", "Size");
+      else
+       {
+         fprintf_unfiltered (file, " %5ld",
+                             regcache->descr->sizeof_register[regnum]);
+         if ((regcache->descr->sizeof_register[regnum]
+              != REGISTER_RAW_SIZE (regnum))
+             || (regcache->descr->sizeof_register[regnum]
+                 != REGISTER_VIRTUAL_SIZE (regnum))
+             || (regcache->descr->sizeof_register[regnum]
+                 != TYPE_LENGTH (REGISTER_VIRTUAL_TYPE (regnum)))
+             )
+           {
+             if (!footnote_register_size)
+               footnote_register_size = ++footnote_nr;
+             fprintf_unfiltered (file, "*%d", footnote_register_size);
+           }
+         else
+           fprintf_unfiltered (file, " ");
+       }
+
+      /* Type.  */
+      if (regnum < 0)
+       fprintf_unfiltered (file, " %-20s", "Type");
+      else
+       {
+         static const char blt[] = "builtin_type";
+         const char *t = TYPE_NAME (REGISTER_VIRTUAL_TYPE (regnum));
+         if (t == NULL)
+           {
+             char *n;
+             if (!footnote_register_type_name_null)
+               footnote_register_type_name_null = ++footnote_nr;
+             xasprintf (&n, "*%d", footnote_register_type_name_null);
+             make_cleanup (xfree, n);
+             t = n;
+           }
+         /* Chop a leading builtin_type.  */
+         if (strncmp (t, blt, strlen (blt)) == 0)
+           t += strlen (blt);
+         fprintf_unfiltered (file, " %-20s", t);
+       }
+
+      /* Value, raw.  */
+      if (what_to_dump == regcache_dump_raw)
+       {
+         if (regnum < 0)
+           fprintf_unfiltered (file, "Raw value");
+         else if (regnum >= regcache->descr->nr_raw_registers)
+           fprintf_unfiltered (file, "<cooked>");
+         else if (!regcache_valid_p (regcache, regnum))
+           fprintf_unfiltered (file, "<invalid>");
+         else
+           {
+             regcache_raw_read (regcache, regnum, buf);
+             fprintf_unfiltered (file, "0x");
+             dump_endian_bytes (file, TARGET_BYTE_ORDER, buf,
+                                REGISTER_RAW_SIZE (regnum));
+           }
+       }
+
+      /* Value, cooked.  */
+      if (what_to_dump == regcache_dump_cooked)
+       {
+         if (regnum < 0)
+           fprintf_unfiltered (file, "Cooked value");
+         else
+           {
+             regcache_cooked_read (regcache, regnum, buf);
+             fprintf_unfiltered (file, "0x");
+             dump_endian_bytes (file, TARGET_BYTE_ORDER, buf,
+                                REGISTER_VIRTUAL_SIZE (regnum));
+           }
+       }
+
+      fprintf_unfiltered (file, "\n");
+    }
+
+  if (footnote_register_size)
+    fprintf_unfiltered (file, "*%d: Inconsistent register sizes.\n",
+                       footnote_register_size);
+  if (footnote_register_offset)
+    fprintf_unfiltered (file, "*%d: Inconsistent register offsets.\n",
+                       footnote_register_offset);
+  if (footnote_register_type_name_null)
+    fprintf_unfiltered (file, 
+                       "*%d: Register type's name NULL.\n",
+                       footnote_register_type_name_null);
+  do_cleanups (cleanups);
+}
+
+static void
+regcache_print (char *args, enum regcache_dump_what what_to_dump)
+{
+  if (args == NULL)
+    regcache_dump (current_regcache, gdb_stdout, what_to_dump);
+  else
+    {
+      struct ui_file *file = gdb_fopen (args, "w");
+      if (file == NULL)
+       perror_with_name ("maintenance print architecture");
+      regcache_dump (current_regcache, file, what_to_dump);    
+      ui_file_delete (file);
+    }
+}
+
+static void
+maintenance_print_registers (char *args, int from_tty)
+{
+  regcache_print (args, regcache_dump_none);
+}
+
+static void
+maintenance_print_raw_registers (char *args, int from_tty)
+{
+  regcache_print (args, regcache_dump_raw);
+}
+
+static void
+maintenance_print_cooked_registers (char *args, int from_tty)
+{
+  regcache_print (args, regcache_dump_cooked);
+}
+
 void
 _initialize_regcache (void)
 {
@@ -1227,4 +1561,21 @@ _initialize_regcache (void)
    /* Initialize the thread/process associated with the current set of
       registers.  For now, -1 is special, and means `no current process'.  */
   registers_ptid = pid_to_ptid (-1);
+
+  add_cmd ("registers", class_maintenance,
+          maintenance_print_registers,
+          "Print the internal register configuration.\
+Takes an optional file parameter.",
+          &maintenanceprintlist);
+  add_cmd ("raw-registers", class_maintenance,
+          maintenance_print_raw_registers,
+          "Print the internal register configuration including raw values.\
+Takes an optional file parameter.",
+          &maintenanceprintlist);
+  add_cmd ("cooked-registers", class_maintenance,
+          maintenance_print_cooked_registers,
+          "Print the internal register configuration including cooked values.\
+Takes an optional file parameter.",
+          &maintenanceprintlist);
+
 }
This page took 0.031875 seconds and 4 git commands to generate.