1 /* event.c: Routines needed for dealing with the event list
2    Copyright (c) 2000-2008 Philip Kendall
3 
4    $Id: event.c 4717 2012-06-07 03:54:45Z fredm $
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20    Author contact information:
21 
22    E-mail: philip-fuse@shadowmagic.org.uk
23 
24 */
25 
26 #include <config.h>
27 
28 #include <string.h>
29 
30 #include <libspectrum.h>
31 
32 #include "event.h"
33 #include "fuse.h"
34 #include "ui/ui.h"
35 #include "utils.h"
36 
37 /* A large value to mean `no events due' */
38 static const libspectrum_dword event_no_events = 0xffffffff;
39 
40 /* When will the next event happen? */
41 libspectrum_dword event_next_event;
42 
43 /* The actual list of events */
44 static GSList *event_list = NULL;
45 
46 /* An event ready to be reused */
47 static event_t *event_free = NULL;
48 
49 /* A null event */
50 int event_type_null;
51 
52 typedef struct event_descriptor_t {
53   event_fn_t fn;
54   char *description;
55 } event_descriptor_t;
56 
57 static GArray *registered_events;
58 
59 void
event_init(void)60 event_init( void )
61 {
62   registered_events = g_array_new( FALSE, FALSE, sizeof( event_descriptor_t ) );
63 
64   event_type_null = event_register( NULL, "[Deleted event]" );
65 
66   event_next_event = event_no_events;
67 }
68 
69 int
event_register(event_fn_t fn,const char * description)70 event_register( event_fn_t fn, const char *description )
71 {
72   event_descriptor_t descriptor;
73 
74   descriptor.fn = fn;
75   descriptor.description = utils_safe_strdup( description );
76 
77   g_array_append_val( registered_events, descriptor );
78 
79   return registered_events->len - 1;
80 }
81 
82 static gint
event_add_cmp(gconstpointer a1,gconstpointer b1)83 event_add_cmp( gconstpointer a1, gconstpointer b1 )
84 {
85   const event_t *a = a1, *b = b1;
86 
87   return a->tstates != b->tstates ? a->tstates - b->tstates
88 		                  : a->type - b->type;
89 }
90 
91 /* Add an event at the correct place in the event list */
92 void
event_add_with_data(libspectrum_dword event_time,int type,void * user_data)93 event_add_with_data( libspectrum_dword event_time, int type, void *user_data )
94 {
95   event_t *ptr;
96 
97   if( event_free ) {
98     ptr = event_free;
99     event_free = NULL;
100   } else {
101     ptr = libspectrum_malloc( sizeof( *ptr ) );
102   }
103 
104   ptr->tstates = event_time;
105   ptr->type =type;
106   ptr->user_data = user_data;
107 
108   if( event_time < event_next_event ) {
109     event_next_event = event_time;
110     event_list = g_slist_prepend( event_list, ptr );
111   } else {
112     event_list = g_slist_insert_sorted( event_list, ptr, event_add_cmp );
113   }
114 }
115 
116 /* Do all events which have passed */
117 int
event_do_events(void)118 event_do_events( void )
119 {
120   event_t *ptr;
121 
122   while(event_next_event <= tstates) {
123     ptr = event_list->data;
124     event_descriptor_t descriptor =
125       g_array_index( registered_events, event_descriptor_t, ptr->type );
126 
127     /* Remove the event from the list *before* processing */
128     event_list = g_slist_remove( event_list, ptr );
129 
130     if( event_list == NULL ) {
131       event_next_event = event_no_events;
132     } else {
133       event_next_event = ((event_t*)(event_list->data))->tstates;
134     }
135 
136     if( descriptor.fn ) descriptor.fn( ptr->tstates, ptr->type, ptr->user_data );
137 
138     if( event_free ) {
139       libspectrum_free( ptr );
140     } else {
141       event_free = ptr;
142     }
143   }
144 
145   return 0;
146 }
147 
148 static void
event_reduce_tstates(gpointer data,gpointer user_data)149 event_reduce_tstates( gpointer data, gpointer user_data )
150 {
151   event_t *ptr = data;
152   libspectrum_dword *tstates_per_frame = user_data;
153 
154   ptr->tstates -= *tstates_per_frame;
155 }
156 
157 /* Called at end of frame to reduce T-state count of all entries */
158 void
event_frame(libspectrum_dword tstates_per_frame)159 event_frame( libspectrum_dword tstates_per_frame )
160 {
161   g_slist_foreach( event_list, event_reduce_tstates, &tstates_per_frame );
162 
163   event_next_event = event_list ?
164     ((event_t*)(event_list->data))->tstates : event_no_events;
165 }
166 
167 /* Do all events that would happen between the current time and when
168    the next interrupt will occur; called only when RZX playback is in
169    effect */
170 void
event_force_events(void)171 event_force_events( void )
172 {
173   while( event_next_event < machine_current->timings.tstates_per_frame ) {
174 
175     /* Jump along to the next event */
176     tstates = event_next_event;
177 
178     /* And do that event */
179     event_do_events();
180 
181   }
182 }
183 
184 static void
set_event_null(gpointer data,gpointer user_data)185 set_event_null( gpointer data, gpointer user_data )
186 {
187   event_t *ptr = data;
188   int *type = user_data;
189 
190   if( ptr->type == *type ) ptr->type = event_type_null;
191 }
192 
193 static void
set_event_null_with_user_data(gpointer data,gpointer user_data)194 set_event_null_with_user_data( gpointer data, gpointer user_data )
195 {
196   event_t *event = data;
197   event_t *template = user_data;
198 
199   if( event->type == template->type && event->user_data == template->user_data )
200     event->type = event_type_null;
201 }
202 
203 /* Remove all events of a specific type from the stack */
204 void
event_remove_type(int type)205 event_remove_type( int type )
206 {
207   g_slist_foreach( event_list, set_event_null, &type );
208 }
209 
210 /* Remove all events of a specific type and user data from the stack */
211 void
event_remove_type_user_data(int type,gpointer user_data)212 event_remove_type_user_data( int type, gpointer user_data )
213 {
214   event_t template;
215 
216   template.type = type;
217   template.user_data = user_data;
218   g_slist_foreach( event_list, set_event_null_with_user_data, &template );
219 }
220 
221 /* Free the memory used by a specific entry */
222 static void
event_free_entry(gpointer data,gpointer user_data GCC_UNUSED)223 event_free_entry( gpointer data, gpointer user_data GCC_UNUSED )
224 {
225   libspectrum_free( data );
226 }
227 
228 /* Clear the event stack */
229 void
event_reset(void)230 event_reset( void )
231 {
232   g_slist_foreach( event_list, event_free_entry, NULL );
233   g_slist_free( event_list );
234   event_list = NULL;
235 
236   event_next_event = event_no_events;
237 
238   libspectrum_free( event_free );
239   event_free = NULL;
240 }
241 
242 /* Call a user-supplied function for every event in the current list */
243 void
event_foreach(GFunc function,gpointer user_data)244 event_foreach( GFunc function, gpointer user_data )
245 {
246   g_slist_foreach( event_list, function, user_data );
247 }
248 
249 /* A textual representation of each event type */
250 const char*
event_name(int type)251 event_name( int type )
252 {
253   return g_array_index( registered_events, event_descriptor_t, type ).description;
254 }
255 
256 void
registered_events_free(void)257 registered_events_free( void )
258 {
259   int i;
260   event_descriptor_t descriptor;
261 
262   if( !registered_events ) return;
263 
264   for( i = 0; i < registered_events->len; i++ ) {
265     descriptor = g_array_index( registered_events, event_descriptor_t, i );
266     libspectrum_free( descriptor.description );
267   }
268 
269   g_array_free( registered_events, TRUE );
270   registered_events = NULL;
271 }
272 
273 /* Tidy-up function called at end of emulation */
274 void
event_end(void)275 event_end( void )
276 {
277   event_reset();
278   registered_events_free();
279 }
280