text: /* Hardware event manager.
     Copyright (C) 1998-2019 Free Software Foundation, Inc.
     Contributed by Cygnus Support.
  
  This file is part of GDB, the GNU debugger.
  
  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 3 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, see <http://www.gnu.org/licenses/>.  */
  
  
  #include "hw-main.h"
  #include "hw-base.h"
  
  #include "sim-events.h"
  
  
  /* The hw-events object is implemented using sim-events */
  
  struct hw_event
  {
    void *data;
    struct hw *me;
    hw_event_callback *callback;
    sim_event *real;
    struct hw_event_data *entry;
  };
  
  struct hw_event_data
  {
    struct hw_event event;
    struct hw_event_data *next;
  };
  
  void
  create_hw_event_data (struct hw *me)
  {
    if (me->events_of_hw != NULL)
      hw_abort (me, "stray events");
    /* NOP */
  }
  
  void
  delete_hw_event_data (struct hw *me)
  {
    /* Remove the scheduled event.  */
    while (me->events_of_hw)
      hw_event_queue_deschedule (me, &me->events_of_hw->event);
  }
  
  
  /* Pass the H/W event onto the real callback */
  
  static void
  bounce_hw_event (SIM_DESC sd,
  		 void *data)
  {
    /* save the data */
    struct hw_event_data *entry = (struct hw_event_data *) data;
    struct hw *me = entry->event.me;
    void *event_data = entry->event.data;
    hw_event_callback *callback = entry->event.callback;
    struct hw_event_data **prev = &me->events_of_hw;
    while ((*prev) != entry)
      prev = &(*prev)->next;
    (*prev) = entry->next;
    hw_free (me, entry);
    callback (me, event_data); /* may not return */
  }
  
  
  
  /* Map onto the event functions */
  
  struct hw_event *
  hw_event_queue_schedule (struct hw *me,
  			 signed64 delta_time,
  			 hw_event_callback *callback,
  			 void *data)
  {
    struct hw_event *event;
    va_list dummy;
    memset (&dummy, 0, sizeof dummy);
    event = hw_event_queue_schedule_vtracef (me, delta_time, callback, data,
  					   NULL, dummy);
    return event;
  }
  
  struct hw_event *
  hw_event_queue_schedule_tracef (struct hw *me,
  				signed64 delta_time,
  				hw_event_callback *callback,
  				void *data,
  				const char *fmt,
  				...)
  {
    struct hw_event *event;
    va_list ap;
    va_start (ap, fmt);
    event = hw_event_queue_schedule_vtracef (me, delta_time, callback, data, fmt, ap);
    va_end (ap);
    return event;
  }
  
  struct hw_event *
  hw_event_queue_schedule_vtracef (struct hw *me,
  				 signed64 delta_time,
  				 hw_event_callback *callback,
  				 void *data,
  				 const char *fmt,
  				 va_list ap)
  {
    struct hw_event_data *entry = HW_ZALLOC (me, struct hw_event_data);
    entry->next = me->events_of_hw;
    me->events_of_hw = entry;
    /* fill it in */
    entry->event.entry = entry;
    entry->event.data = data;
    entry->event.callback = callback;
    entry->event.me = me;
    entry->event.real = sim_events_schedule_vtracef (hw_system (me),
  						   delta_time,
  						   bounce_hw_event,
  						   entry,
  						   fmt, ap);
    return &entry->event;
  }
  
  
  void
  hw_event_queue_deschedule (struct hw *me,
  			   struct hw_event *event_to_remove)
  {
  /* ZAP the event but only if it is still in the event queue.  Note
     that event_to_remove is only de-referenced after its validity has
     been confirmed.  */
    struct hw_event_data **prev;
    for (prev = &me->events_of_hw;
         (*prev) != NULL;
         prev = &(*prev)->next)
      {
        struct hw_event_data *entry = (*prev);
        if (&entry->event == event_to_remove)
  	{
  	  sim_events_deschedule (hw_system (me),
  				 entry->event.real);
  	  (*prev) = entry->next;
  	  hw_free (me, entry);
  	  return;
  	}
      }
  }
  
  
  signed64
  hw_event_queue_time (struct hw *me)
  {
    return sim_events_time (hw_system (me));
  }
  
  /* Returns the time that remains before the event is raised. */
  signed64
  hw_event_remain_time (struct hw *me, struct hw_event *event)
  {
    signed64 t;
  
    t = sim_events_remain_time (hw_system (me), event->real);
    return t;
  }
  
  /* Only worry about this compling on ANSI systems.
     Build with `make test-hw-events' in sim/<cpu> directory*/
  
  #if defined (MAIN)
  #include "sim-main.h"
  #include <string.h>
  #include <stdio.h>
  
  static void
  test_handler (struct hw *me,
  	      void *data)
  {
    int *n = data;
    if (*n != hw_event_queue_time (me))
      abort ();
    *n = -(*n);
  }
  
  int
  main (int argc,
        char **argv)
  {
    host_callback *cb = ZALLOC (host_callback);
    struct sim_state *sd = sim_state_alloc (0, cb);
    struct hw *me = ZALLOC (struct hw);
    sim_pre_argv_init (sd, "test-hw-events");
    sim_post_argv_init (sd);
    me->system_of_hw = sd;
  
    printf ("Create hw-event-data\n");
    {
      create_hw_alloc_data (me);
      create_hw_event_data (me);
      delete_hw_event_data (me);
      delete_hw_alloc_data (me);
    }
  
    printf ("Create hw-events\n");
    {
      struct hw_event *a;
      struct hw_event *b;
      struct hw_event *c;
      struct hw_event *d;
      create_hw_alloc_data (me);
      create_hw_event_data (me);
      a = hw_event_queue_schedule (me, 0, NULL, NULL);
      b = hw_event_queue_schedule (me, 1, NULL, NULL);
      c = hw_event_queue_schedule (me, 2, NULL, NULL);
      d = hw_event_queue_schedule (me, 1, NULL, NULL);
      hw_event_queue_deschedule (me, c);
      hw_event_queue_deschedule (me, b);
      hw_event_queue_deschedule (me, a);
      hw_event_queue_deschedule (me, d);
      c = HW_ZALLOC (me, struct hw_event);
      hw_event_queue_deschedule (me, b); /* OOPS! */
      hw_free (me, c);
      delete_hw_event_data (me);
      delete_hw_alloc_data (me);
    }
  
    printf ("Schedule hw-events\n");
    {
      struct hw_event **e;
      int *n;
      int i;
      int nr = 4;
      e = HW_NZALLOC (me, struct hw_event *, nr);
      n = HW_NZALLOC (me, int, nr);
      create_hw_alloc_data (me);
      create_hw_event_data (me);
      for (i = 0; i < nr; i++)
        {
  	n[i] = i;
  	e[i] = hw_event_queue_schedule (me, i, test_handler, &n[i]);
        }
      sim_events_preprocess (sd, 1, 1);
      for (i = 0; i < nr; i++)
        {
  	if (sim_events_tick (sd))
  	  sim_events_process (sd);
        }
      for (i = 0; i < nr; i++)
        {
  	if (n[i] != -i)
  	  abort ();
  	hw_event_queue_deschedule (me, e[i]);
        }
      hw_free (me, n);
      hw_free (me, e);
      delete_hw_event_data (me);
      delete_hw_alloc_data (me);
    }
  
    return 0;
  }
  #endif
  