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