xref: /netbsd-src/external/gpl3/gdb/dist/sim/common/sim-watch.c (revision 88241920d21b339bf319c0e979ffda80c49a2936)
14e98e3e1Schristos /* Generic simulator watchpoint support.
2*88241920Schristos    Copyright (C) 1997-2024 Free Software Foundation, Inc.
34e98e3e1Schristos    Contributed by Cygnus Support.
44e98e3e1Schristos 
54e98e3e1Schristos This file is part of GDB, the GNU debugger.
64e98e3e1Schristos 
74e98e3e1Schristos This program is free software; you can redistribute it and/or modify
84e98e3e1Schristos it under the terms of the GNU General Public License as published by
94e98e3e1Schristos the Free Software Foundation; either version 3 of the License, or
104e98e3e1Schristos (at your option) any later version.
114e98e3e1Schristos 
124e98e3e1Schristos This program is distributed in the hope that it will be useful,
134e98e3e1Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
144e98e3e1Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
154e98e3e1Schristos GNU General Public License for more details.
164e98e3e1Schristos 
174e98e3e1Schristos You should have received a copy of the GNU General Public License
184e98e3e1Schristos along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
194e98e3e1Schristos 
204b169a6bSchristos /* This must come before any other includes.  */
214b169a6bSchristos #include "defs.h"
224e98e3e1Schristos 
234e98e3e1Schristos #include <ctype.h>
248dffb485Schristos #include <stdio.h>
254e98e3e1Schristos #include <stdlib.h>
264b169a6bSchristos #include <string.h>
274b169a6bSchristos 
284b169a6bSchristos #include "libiberty.h"
294b169a6bSchristos 
304b169a6bSchristos #include "sim-main.h"
314b169a6bSchristos #include "sim-options.h"
324b169a6bSchristos #include "sim-signal.h"
334b169a6bSchristos #include "sim-assert.h"
344e98e3e1Schristos 
354e98e3e1Schristos enum {
364e98e3e1Schristos   OPTION_WATCH_DELETE                      = OPTION_START,
374e98e3e1Schristos 
384e98e3e1Schristos   OPTION_WATCH_INFO,
394e98e3e1Schristos   OPTION_WATCH_CLOCK,
404e98e3e1Schristos   OPTION_WATCH_CYCLES,
414e98e3e1Schristos   OPTION_WATCH_PC,
424e98e3e1Schristos 
434e98e3e1Schristos   OPTION_WATCH_OP,
444e98e3e1Schristos };
454e98e3e1Schristos 
464e98e3e1Schristos 
474e98e3e1Schristos /* Break an option number into its op/int-nr */
484e98e3e1Schristos static watchpoint_type
494e98e3e1Schristos option_to_type (SIM_DESC sd,
504e98e3e1Schristos 		int option)
514e98e3e1Schristos {
524e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
534e98e3e1Schristos   watchpoint_type type = ((option - OPTION_WATCH_OP)
544e98e3e1Schristos 			  / (watch->nr_interrupts + 1));
554e98e3e1Schristos   SIM_ASSERT (type >= 0 && type < nr_watchpoint_types);
564e98e3e1Schristos   return type;
574e98e3e1Schristos }
584e98e3e1Schristos 
594e98e3e1Schristos static int
604e98e3e1Schristos option_to_interrupt_nr (SIM_DESC sd,
614e98e3e1Schristos 			int option)
624e98e3e1Schristos {
634e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
644e98e3e1Schristos   int interrupt_nr = ((option - OPTION_WATCH_OP)
654e98e3e1Schristos 		      % (watch->nr_interrupts + 1));
664e98e3e1Schristos   return interrupt_nr;
674e98e3e1Schristos }
684e98e3e1Schristos 
694e98e3e1Schristos static int
704e98e3e1Schristos type_to_option (SIM_DESC sd,
714e98e3e1Schristos 		watchpoint_type type,
724e98e3e1Schristos 		int interrupt_nr)
734e98e3e1Schristos {
744e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
754e98e3e1Schristos   return ((type * (watch->nr_interrupts + 1))
764e98e3e1Schristos 	  + interrupt_nr
774e98e3e1Schristos 	  + OPTION_WATCH_OP);
784e98e3e1Schristos }
794e98e3e1Schristos 
804e98e3e1Schristos 
814e98e3e1Schristos /* Delete one or more watchpoints.  Fail if no watchpoints were found */
824e98e3e1Schristos 
834e98e3e1Schristos static SIM_RC
844e98e3e1Schristos do_watchpoint_delete (SIM_DESC sd,
854e98e3e1Schristos 		      int ident,
864e98e3e1Schristos 		      watchpoint_type type)
874e98e3e1Schristos {
884e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
894e98e3e1Schristos   sim_watch_point **entry = &watch->points;
904e98e3e1Schristos   SIM_RC status = SIM_RC_FAIL;
914e98e3e1Schristos   while ((*entry) != NULL)
924e98e3e1Schristos     {
934e98e3e1Schristos       if ((*entry)->ident == ident
944e98e3e1Schristos 	  || (*entry)->type == type)
954e98e3e1Schristos 	{
964e98e3e1Schristos 	  sim_watch_point *dead = (*entry);
974e98e3e1Schristos 	  (*entry) = (*entry)->next;
984e98e3e1Schristos 	  sim_events_deschedule (sd, dead->event);
994e98e3e1Schristos 	  free (dead);
1004e98e3e1Schristos 	  status = SIM_RC_OK;
1014e98e3e1Schristos 	}
1024e98e3e1Schristos       else
1034e98e3e1Schristos 	entry = &(*entry)->next;
1044e98e3e1Schristos     }
1054e98e3e1Schristos   return status;
1064e98e3e1Schristos }
1074e98e3e1Schristos 
1084e98e3e1Schristos static const char *
1094e98e3e1Schristos watchpoint_type_to_str (SIM_DESC sd,
1104e98e3e1Schristos 			watchpoint_type type)
1114e98e3e1Schristos {
1124e98e3e1Schristos   switch (type)
1134e98e3e1Schristos     {
1144e98e3e1Schristos     case  pc_watchpoint:
1154e98e3e1Schristos       return "pc";
1164e98e3e1Schristos     case clock_watchpoint:
1174e98e3e1Schristos       return "clock";
1184e98e3e1Schristos     case cycles_watchpoint:
1194e98e3e1Schristos       return "cycles";
1204e98e3e1Schristos     case invalid_watchpoint:
1214e98e3e1Schristos     case nr_watchpoint_types:
1224e98e3e1Schristos       return "(invalid-type)";
1234e98e3e1Schristos     }
1244e98e3e1Schristos   return NULL;
1254e98e3e1Schristos }
1264e98e3e1Schristos 
1274e98e3e1Schristos static const char *
1284e98e3e1Schristos interrupt_nr_to_str (SIM_DESC sd,
1294e98e3e1Schristos 		     int interrupt_nr)
1304e98e3e1Schristos {
1314e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
1324e98e3e1Schristos   if (interrupt_nr < 0)
1334e98e3e1Schristos     return "(invalid-interrupt)";
1344e98e3e1Schristos   else if (interrupt_nr >= watch->nr_interrupts)
1354e98e3e1Schristos     return "breakpoint";
1364e98e3e1Schristos   else
1374e98e3e1Schristos     return watch->interrupt_names[interrupt_nr];
1384e98e3e1Schristos }
1394e98e3e1Schristos 
1404e98e3e1Schristos 
1414e98e3e1Schristos static void
1424e98e3e1Schristos do_watchpoint_info (SIM_DESC sd)
1434e98e3e1Schristos {
1444e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
1454e98e3e1Schristos   sim_watch_point *point;
1464e98e3e1Schristos   sim_io_printf (sd, "Watchpoints:\n");
1474e98e3e1Schristos   for (point = watch->points; point != NULL; point = point->next)
1484e98e3e1Schristos     {
1494e98e3e1Schristos       sim_io_printf (sd, "%3d: watch %s %s ",
1504e98e3e1Schristos 		     point->ident,
1514e98e3e1Schristos 		     watchpoint_type_to_str (sd, point->type),
1524e98e3e1Schristos 		     interrupt_nr_to_str (sd, point->interrupt_nr));
1534e98e3e1Schristos       if (point->is_periodic)
1544e98e3e1Schristos 	sim_io_printf (sd, "+");
1554e98e3e1Schristos       if (!point->is_within)
1564e98e3e1Schristos 	sim_io_printf (sd, "!");
1574e98e3e1Schristos       sim_io_printf (sd, "0x%lx", point->arg0);
1584e98e3e1Schristos       if (point->arg1 != point->arg0)
1594e98e3e1Schristos 	sim_io_printf (sd, ",0x%lx", point->arg1);
1604e98e3e1Schristos       sim_io_printf (sd, "\n");
1614e98e3e1Schristos     }
1624e98e3e1Schristos }
1634e98e3e1Schristos 
1644e98e3e1Schristos 
1654e98e3e1Schristos 
1664e98e3e1Schristos static sim_event_handler handle_watchpoint;
1674e98e3e1Schristos 
1684e98e3e1Schristos static SIM_RC
1694e98e3e1Schristos schedule_watchpoint (SIM_DESC sd,
1704e98e3e1Schristos 		     sim_watch_point *point)
1714e98e3e1Schristos {
1724e98e3e1Schristos   switch (point->type)
1734e98e3e1Schristos     {
1744e98e3e1Schristos     case pc_watchpoint:
1754b169a6bSchristos       point->event = sim_events_watch_pc (sd,
1764e98e3e1Schristos 					  point->is_within,
1774e98e3e1Schristos 					  point->arg0, point->arg1,
1784e98e3e1Schristos 					  /* PC in arg0..arg1 */
1794e98e3e1Schristos 					  handle_watchpoint,
1804e98e3e1Schristos 					  point);
1814e98e3e1Schristos       return SIM_RC_OK;
1824e98e3e1Schristos     case clock_watchpoint:
1834e98e3e1Schristos       point->event = sim_events_watch_clock (sd,
1844e98e3e1Schristos 					     point->arg0, /* ms time */
1854e98e3e1Schristos 					     handle_watchpoint,
1864e98e3e1Schristos 					     point);
1874e98e3e1Schristos       return SIM_RC_OK;
1884e98e3e1Schristos     case cycles_watchpoint:
1894e98e3e1Schristos       point->event = sim_events_schedule (sd,
1904e98e3e1Schristos 					  point->arg0, /* time */
1914e98e3e1Schristos 					  handle_watchpoint,
1924e98e3e1Schristos 					  point);
1934e98e3e1Schristos       return SIM_RC_OK;
1944e98e3e1Schristos     default:
1954e98e3e1Schristos       sim_engine_abort (sd, NULL, NULL_CIA,
1964e98e3e1Schristos 			"handle_watchpoint - internal error - bad switch");
1974e98e3e1Schristos       return SIM_RC_FAIL;
1984e98e3e1Schristos     }
1994e98e3e1Schristos   return SIM_RC_OK;
2004e98e3e1Schristos }
2014e98e3e1Schristos 
2024e98e3e1Schristos 
2034e98e3e1Schristos static void
2044e98e3e1Schristos handle_watchpoint (SIM_DESC sd, void *data)
2054e98e3e1Schristos {
2064e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
2074e98e3e1Schristos   sim_watch_point *point = (sim_watch_point *) data;
2084e98e3e1Schristos   int interrupt_nr = point->interrupt_nr;
2094e98e3e1Schristos 
2104e98e3e1Schristos   if (point->is_periodic)
2114e98e3e1Schristos     /* reschedule this event before processing it */
2124e98e3e1Schristos     schedule_watchpoint (sd, point);
2134e98e3e1Schristos   else
2144e98e3e1Schristos     do_watchpoint_delete (sd, point->ident, invalid_watchpoint);
2154e98e3e1Schristos 
2164e98e3e1Schristos   if (point->interrupt_nr == watch->nr_interrupts)
2174e98e3e1Schristos     sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGINT);
2184e98e3e1Schristos   else
2194e98e3e1Schristos     watch->interrupt_handler (sd, &watch->interrupt_names[interrupt_nr]);
2204e98e3e1Schristos }
2214e98e3e1Schristos 
2224e98e3e1Schristos 
2234e98e3e1Schristos static SIM_RC
2244e98e3e1Schristos do_watchpoint_create (SIM_DESC sd,
2254e98e3e1Schristos 		      watchpoint_type type,
2264e98e3e1Schristos 		      int opt,
2274e98e3e1Schristos 		      char *arg)
2284e98e3e1Schristos {
2294e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
2304e98e3e1Schristos   sim_watch_point **point;
2314e98e3e1Schristos 
2324e98e3e1Schristos   /* create the watchpoint */
2334e98e3e1Schristos   point = &watch->points;
2344e98e3e1Schristos   while ((*point) != NULL)
2354e98e3e1Schristos     point = &(*point)->next;
2364e98e3e1Schristos   (*point) = ZALLOC (sim_watch_point);
2374e98e3e1Schristos 
2384e98e3e1Schristos   /* fill in the details */
2394e98e3e1Schristos   (*point)->ident = ++(watch->last_point_nr);
2404e98e3e1Schristos   (*point)->type = option_to_type (sd, opt);
2414e98e3e1Schristos   (*point)->interrupt_nr = option_to_interrupt_nr (sd, opt);
2424e98e3e1Schristos   /* prefixes to arg - +== periodic, !==not or outside */
2434e98e3e1Schristos   (*point)->is_within = 1;
2444e98e3e1Schristos   while (1)
2454e98e3e1Schristos     {
2464e98e3e1Schristos       if (arg[0] == '+')
2474e98e3e1Schristos 	(*point)->is_periodic = 1;
2484e98e3e1Schristos       else if (arg[0] == '!')
2494e98e3e1Schristos 	(*point)->is_within = 0;
2504e98e3e1Schristos       else
2514e98e3e1Schristos 	break;
2524e98e3e1Schristos       arg++;
2534e98e3e1Schristos     }
2544e98e3e1Schristos 
2554e98e3e1Schristos   (*point)->arg0 = strtoul (arg, &arg, 0);
2564e98e3e1Schristos   if (arg[0] == ',')
2574b169a6bSchristos     (*point)->arg1 = strtoul (arg + 1, NULL, 0);
2584e98e3e1Schristos   else
2594e98e3e1Schristos     (*point)->arg1 = (*point)->arg0;
2604e98e3e1Schristos 
2614e98e3e1Schristos   /* schedule it */
2624e98e3e1Schristos   schedule_watchpoint (sd, (*point));
2634e98e3e1Schristos 
2644e98e3e1Schristos   return SIM_RC_OK;
2654e98e3e1Schristos }
2664e98e3e1Schristos 
2674e98e3e1Schristos 
2684e98e3e1Schristos static SIM_RC
2694e98e3e1Schristos watchpoint_option_handler (SIM_DESC sd, sim_cpu *cpu, int opt,
2704e98e3e1Schristos 			   char *arg, int is_command)
2714e98e3e1Schristos {
2724e98e3e1Schristos   if (opt >= OPTION_WATCH_OP)
2734e98e3e1Schristos     return do_watchpoint_create (sd, clock_watchpoint, opt, arg);
2744e98e3e1Schristos   else
2754e98e3e1Schristos     switch (opt)
2764e98e3e1Schristos       {
2774e98e3e1Schristos 
2784e98e3e1Schristos       case OPTION_WATCH_DELETE:
2794e98e3e1Schristos 	if (isdigit ((int) arg[0]))
2804e98e3e1Schristos 	  {
2814e98e3e1Schristos 	    int ident = strtol (arg, NULL, 0);
2824e98e3e1Schristos 	    if (do_watchpoint_delete (sd, ident, invalid_watchpoint)
2834e98e3e1Schristos 		!= SIM_RC_OK)
2844e98e3e1Schristos 	      {
2854e98e3e1Schristos 		sim_io_eprintf (sd, "Watchpoint %d not found\n", ident);
2864e98e3e1Schristos 		return SIM_RC_FAIL;
2874e98e3e1Schristos 	      }
2884e98e3e1Schristos 	    return SIM_RC_OK;
2894e98e3e1Schristos 	  }
2904e98e3e1Schristos 	else if (strcasecmp (arg, "all") == 0)
2914e98e3e1Schristos 	  {
2924e98e3e1Schristos 	    watchpoint_type type;
2934e98e3e1Schristos 	    for (type = invalid_watchpoint + 1;
2944e98e3e1Schristos 		 type < nr_watchpoint_types;
2954e98e3e1Schristos 		 type++)
2964e98e3e1Schristos 	      {
2974e98e3e1Schristos 		do_watchpoint_delete (sd, 0, type);
2984e98e3e1Schristos 	      }
2994e98e3e1Schristos 	    return SIM_RC_OK;
3004e98e3e1Schristos 	  }
3014e98e3e1Schristos 	else if (strcasecmp (arg, "pc") == 0)
3024e98e3e1Schristos 	  {
3034e98e3e1Schristos 	    if (do_watchpoint_delete (sd, 0, pc_watchpoint)
3044e98e3e1Schristos 		!= SIM_RC_OK)
3054e98e3e1Schristos 	      {
3064e98e3e1Schristos 		sim_io_eprintf (sd, "No PC watchpoints found\n");
3074e98e3e1Schristos 		return SIM_RC_FAIL;
3084e98e3e1Schristos 	      }
3094e98e3e1Schristos 	    return SIM_RC_OK;
3104e98e3e1Schristos 	  }
3114e98e3e1Schristos 	else if (strcasecmp (arg, "clock") == 0)
3124e98e3e1Schristos 	  {
3134e98e3e1Schristos 	    if (do_watchpoint_delete (sd, 0, clock_watchpoint) != SIM_RC_OK)
3144e98e3e1Schristos 	      {
3154e98e3e1Schristos 		sim_io_eprintf (sd, "No CLOCK watchpoints found\n");
3164e98e3e1Schristos 		return SIM_RC_FAIL;
3174e98e3e1Schristos 	      }
3184e98e3e1Schristos 	    return SIM_RC_OK;
3194e98e3e1Schristos 	  }
3204e98e3e1Schristos 	else if (strcasecmp (arg, "cycles") == 0)
3214e98e3e1Schristos 	  {
3224e98e3e1Schristos 	    if (do_watchpoint_delete (sd, 0, cycles_watchpoint) != SIM_RC_OK)
3234e98e3e1Schristos 	      {
3244e98e3e1Schristos 		sim_io_eprintf (sd, "No CYCLES watchpoints found\n");
3254e98e3e1Schristos 		return SIM_RC_FAIL;
3264e98e3e1Schristos 	      }
3274e98e3e1Schristos 	    return SIM_RC_OK;
3284e98e3e1Schristos 	  }
3294e98e3e1Schristos 	sim_io_eprintf (sd, "Unknown watchpoint type `%s'\n", arg);
3304e98e3e1Schristos 	return SIM_RC_FAIL;
3314e98e3e1Schristos 
3324e98e3e1Schristos       case OPTION_WATCH_INFO:
3334e98e3e1Schristos 	{
3344e98e3e1Schristos 	  do_watchpoint_info (sd);
3354e98e3e1Schristos 	  return SIM_RC_OK;
3364e98e3e1Schristos 	}
3374e98e3e1Schristos 
3384e98e3e1Schristos       default:
3394e98e3e1Schristos 	sim_io_eprintf (sd, "Unknown watch option %d\n", opt);
3404e98e3e1Schristos 	return SIM_RC_FAIL;
3414e98e3e1Schristos 
3424e98e3e1Schristos       }
3434e98e3e1Schristos 
3444e98e3e1Schristos }
3454e98e3e1Schristos 
3464e98e3e1Schristos 
3474e98e3e1Schristos static SIM_RC
3484e98e3e1Schristos sim_watchpoint_init (SIM_DESC sd)
3494e98e3e1Schristos {
3504e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
3514e98e3e1Schristos   sim_watch_point *point;
3524e98e3e1Schristos   /* NOTE: Do not need to de-schedule any previous watchpoints as
3534e98e3e1Schristos      sim-events has already done this */
3544e98e3e1Schristos   /* schedule any watchpoints enabled by command line options */
3554e98e3e1Schristos   for (point = watch->points; point != NULL; point = point->next)
3564e98e3e1Schristos     {
3574e98e3e1Schristos       schedule_watchpoint (sd, point);
3584e98e3e1Schristos     }
3594e98e3e1Schristos   return SIM_RC_OK;
3604e98e3e1Schristos }
3614e98e3e1Schristos 
3624e98e3e1Schristos 
3634e98e3e1Schristos static const OPTION watchpoint_options[] =
3644e98e3e1Schristos {
3654e98e3e1Schristos   { {"watch-delete", required_argument, NULL, OPTION_WATCH_DELETE },
3664e98e3e1Schristos       '\0', "IDENT|all|pc|cycles|clock", "Delete a watchpoint",
3674e98e3e1Schristos       watchpoint_option_handler, NULL },
3684e98e3e1Schristos 
3694e98e3e1Schristos   { {"watch-info", no_argument, NULL, OPTION_WATCH_INFO },
3704e98e3e1Schristos       '\0', NULL, "List scheduled watchpoints",
3714e98e3e1Schristos       watchpoint_option_handler, NULL },
3724e98e3e1Schristos 
3734e98e3e1Schristos   { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL }
3744e98e3e1Schristos };
3754e98e3e1Schristos 
3764e98e3e1Schristos static const char *default_interrupt_names[] = { "int", 0, };
3774e98e3e1Schristos 
3784b169a6bSchristos /* This default handler is "good enough" for targets that just want to trap into
3794b169a6bSchristos    gdb when watchpoints are hit, and have only configured the STATE_WATCHPOINTS
3804b169a6bSchristos    pc field.  */
3814b169a6bSchristos static void
3824b169a6bSchristos default_interrupt_handler (SIM_DESC sd, void *data)
3834b169a6bSchristos {
3844b169a6bSchristos   sim_cpu *cpu = STATE_CPU (sd, 0);
3854b169a6bSchristos   address_word cia = CPU_PC_GET (cpu);
3864b169a6bSchristos   sim_engine_halt (sd, cpu, NULL, cia, sim_stopped, SIM_SIGTRAP);
3874b169a6bSchristos }
3884e98e3e1Schristos 
3894e98e3e1Schristos SIM_RC
3904e98e3e1Schristos sim_watchpoint_install (SIM_DESC sd)
3914e98e3e1Schristos {
3924e98e3e1Schristos   sim_watchpoints *watch = STATE_WATCHPOINTS (sd);
3934e98e3e1Schristos   SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
3944e98e3e1Schristos   /* the basic command set */
3954e98e3e1Schristos   sim_module_add_init_fn (sd, sim_watchpoint_init);
3964e98e3e1Schristos   sim_add_option_table (sd, NULL, watchpoint_options);
3974e98e3e1Schristos   /* fill in some details */
3984e98e3e1Schristos   if (watch->interrupt_names == NULL)
3994e98e3e1Schristos     watch->interrupt_names = default_interrupt_names;
4004b169a6bSchristos   if (watch->interrupt_handler == NULL)
4014b169a6bSchristos     watch->interrupt_handler = default_interrupt_handler;
4024e98e3e1Schristos   watch->nr_interrupts = 0;
4034e98e3e1Schristos   while (watch->interrupt_names[watch->nr_interrupts] != NULL)
4044e98e3e1Schristos     watch->nr_interrupts++;
4054e98e3e1Schristos   /* generate more advansed commands */
4064e98e3e1Schristos   {
4074e98e3e1Schristos     OPTION *int_options = NZALLOC (OPTION, 1 + (watch->nr_interrupts + 1) * nr_watchpoint_types);
4084e98e3e1Schristos     int interrupt_nr;
4094e98e3e1Schristos     for (interrupt_nr = 0; interrupt_nr <= watch->nr_interrupts; interrupt_nr++)
4104e98e3e1Schristos       {
4114e98e3e1Schristos 	watchpoint_type type;
4124e98e3e1Schristos 	for (type = 0; type < nr_watchpoint_types; type++)
4134e98e3e1Schristos 	  {
4144e98e3e1Schristos 	    char *name;
4154e98e3e1Schristos 	    int nr = interrupt_nr * nr_watchpoint_types + type;
4164e98e3e1Schristos 	    OPTION *option = &int_options[nr];
4174e98e3e1Schristos 	    if (asprintf (&name, "watch-%s-%s",
4184e98e3e1Schristos 			  watchpoint_type_to_str (sd, type),
4194e98e3e1Schristos 			  interrupt_nr_to_str (sd, interrupt_nr)) < 0)
4204e98e3e1Schristos 	      return SIM_RC_FAIL;
4214e98e3e1Schristos 	    option->opt.name = name;
4224e98e3e1Schristos 	    option->opt.has_arg = required_argument;
4234e98e3e1Schristos 	    option->opt.val = type_to_option (sd, type, interrupt_nr);
4244e98e3e1Schristos 	    option->doc = "";
4254e98e3e1Schristos 	    option->doc_name = "";
4264e98e3e1Schristos 	    option->handler = watchpoint_option_handler;
4274e98e3e1Schristos 	  }
4284e98e3e1Schristos       }
4294e98e3e1Schristos     /* adjust first few entries so that they contain real
4304e98e3e1Schristos        documentation, the first entry includes a list of actions. */
4314e98e3e1Schristos     {
4324e98e3e1Schristos       const char *prefix =
4334e98e3e1Schristos 	"Watch the simulator, take ACTION in COUNT cycles (`+' for every COUNT cycles), ACTION is";
4344e98e3e1Schristos       char *doc;
4354e98e3e1Schristos       int len = strlen (prefix) + 1;
4364e98e3e1Schristos       for (interrupt_nr = 0; interrupt_nr <= watch->nr_interrupts; interrupt_nr++)
4374e98e3e1Schristos 	len += strlen (interrupt_nr_to_str (sd, interrupt_nr)) + 1;
4384e98e3e1Schristos       doc = NZALLOC (char, len);
4394e98e3e1Schristos       strcpy (doc, prefix);
4404e98e3e1Schristos       for (interrupt_nr = 0; interrupt_nr <= watch->nr_interrupts; interrupt_nr++)
4414e98e3e1Schristos 	{
4424e98e3e1Schristos 	  strcat (doc, " ");
4434e98e3e1Schristos 	  strcat (doc, interrupt_nr_to_str (sd, interrupt_nr));
4444e98e3e1Schristos 	}
4454e98e3e1Schristos       int_options[0].doc_name = "watch-cycles-ACTION";
4464e98e3e1Schristos       int_options[0].arg = "[+]COUNT";
4474e98e3e1Schristos       int_options[0].doc = doc;
4484e98e3e1Schristos     }
4494e98e3e1Schristos     int_options[1].doc_name = "watch-pc-ACTION";
4504e98e3e1Schristos     int_options[1].arg = "[!]ADDRESS";
4514e98e3e1Schristos     int_options[1].doc =
4524e98e3e1Schristos       "Watch the PC, take ACTION when matches ADDRESS (in range ADDRESS,ADDRESS), `!' negates test";
4534e98e3e1Schristos     int_options[2].doc_name = "watch-clock-ACTION";
4544e98e3e1Schristos     int_options[2].arg = "[+]MILLISECONDS";
4554e98e3e1Schristos     int_options[2].doc =
4564e98e3e1Schristos       "Watch the clock, take ACTION after MILLISECONDS (`+' for every MILLISECONDS)";
4574e98e3e1Schristos 
4584e98e3e1Schristos     sim_add_option_table (sd, NULL, int_options);
4594e98e3e1Schristos   }
4604e98e3e1Schristos   return SIM_RC_OK;
4614e98e3e1Schristos }
462