1 /* timer.c: Speed routines for Fuse
2    Copyright (c) 1999-2008 Philip Kendall, Marek Januszewski, Fredrick Meunier
3 
4    $Id: timer.c 4664 2012-02-12 11:51:01Z 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 "event.h"
29 #include "movie.h"
30 #include "settings.h"
31 #include "sound.h"
32 #include "tape.h"
33 #include "timer.h"
34 #include "ui/ui.h"
35 
36 static void timer_frame_callback_sound( libspectrum_dword last_tstates );
37 
38 /*
39  * Routines for estimating emulation speed
40  */
41 
42 /* The actual time at the end of each of the last 10 emulated seconds */
43 static double stored_times[10];
44 
45 /* Which is the next entry in 'stored_times' that we will update */
46 static size_t next_stored_time;
47 
48 /* The number of frames until we next update 'stored_times' */
49 static int frames_until_update;
50 
51 /* The number of time samples we have for estimating speed */
52 static int samples;
53 
54 float current_speed = 100.0;
55 
56 static double start_time;
57 
58 static const int TEN_MS = 10;
59 
60 int timer_event;
61 
62 static void timer_frame( libspectrum_dword last_tstates, int event GCC_UNUSED,
63 			 void *user_data GCC_UNUSED );
64 
65 int
timer_estimate_speed(void)66 timer_estimate_speed( void )
67 {
68   double current_time;
69 
70   if( frames_until_update-- ) return 0;
71 
72   current_time = timer_get_time();
73   if( current_time < 0 ) return 1;
74 
75   if( samples < 10 ) {
76 
77     /* If we don't have enough data, assume we're running at the desired
78        speed :-) */
79     current_speed = settings_current.emulation_speed;
80 
81   } else {
82     current_speed = 10 * 100 /
83                       ( current_time - stored_times[ next_stored_time ] );
84   }
85 
86   ui_statusbar_update_speed( current_speed );
87 
88   stored_times[ next_stored_time ] = current_time;
89 
90   next_stored_time = ( next_stored_time + 1 ) % 10;
91   frames_until_update =
92     ( machine_current->timings.processor_speed /
93     machine_current->timings.tstates_per_frame ) - 1;
94 
95   samples++;
96 
97   return 0;
98 }
99 
100 int
timer_estimate_reset(void)101 timer_estimate_reset( void )
102 {
103   start_time = timer_get_time(); if( start_time < 0 ) return 1;
104   samples = 0;
105   next_stored_time = 0;
106   frames_until_update = 0;
107 
108   return 0;
109 }
110 
111 int
timer_init(void)112 timer_init( void )
113 {
114   start_time = timer_get_time(); if( start_time < 0 ) return 1;
115 
116   timer_event = event_register( timer_frame, "Timer" );
117 
118   event_add( 0, timer_event );
119 
120   return 0;
121 }
122 
123 void
timer_end(void)124 timer_end( void )
125 {
126   event_remove_type( timer_event );
127 }
128 
129 #ifdef SOUND_FIFO
130 
131 /* Callback-style sound based timer */
132 #include "sound/sfifo.h"
133 
134 extern sfifo_t sound_fifo;
135 
136 static void
timer_frame_callback_sound(libspectrum_dword last_tstates)137 timer_frame_callback_sound( libspectrum_dword last_tstates )
138 {
139   for(;;) {
140 
141     /* Sleep while fifo is full */
142     if( sfifo_space( &sound_fifo ) < sound_framesiz ) {
143       timer_sleep( TEN_MS );
144     } else {
145       break;
146     }
147 
148   }
149 
150   event_add( last_tstates + machine_current->timings.tstates_per_frame,
151              timer_event );
152 }
153 
154 #else                           /* #ifdef SOUND_FIFO */
155 
156 /* Blocking socket-style sound based timer */
157 static void
timer_frame_callback_sound(libspectrum_dword last_tstates)158 timer_frame_callback_sound( libspectrum_dword last_tstates )
159 {
160   event_add( last_tstates + machine_current->timings.tstates_per_frame,
161              timer_event );
162 }
163 
164 #endif                          /* #ifdef SOUND_FIFO */
165 
166 static void
timer_frame(libspectrum_dword last_tstates,int event GCC_UNUSED,void * user_data GCC_UNUSED)167 timer_frame( libspectrum_dword last_tstates, int event GCC_UNUSED,
168 	     void *user_data GCC_UNUSED )
169 {
170   double current_time, difference;
171   long tstates;
172 
173   if( sound_enabled && settings_current.sound ) {
174     timer_frame_callback_sound( last_tstates );
175     return;
176   }
177 
178   /* If we're fastloading, just schedule another check in a frame's time
179      and do nothing else */
180   if( settings_current.fastload && tape_is_playing() ) {
181 
182     libspectrum_dword next_check_time =
183       last_tstates + machine_current->timings.tstates_per_frame;
184 
185     event_add( next_check_time, timer_event );
186 
187   } else {
188 
189     float speed = ( settings_current.emulation_speed < 1 ?
190                     1.0                                  :
191                     settings_current.emulation_speed ) / 100.0;
192 
193     while( 1 ) {
194 
195       current_time = timer_get_time(); if( current_time < 0 ) return;
196       difference = current_time - start_time;
197 
198       /* Sleep while we are still 10ms ahead */
199       if( difference < 0 ) {
200         timer_sleep( TEN_MS );
201       } else {
202 	break;
203       }
204 
205     }
206 
207     current_time = timer_get_time(); if( current_time < 0 ) return;
208     difference = current_time - start_time;
209 
210     tstates = ( ( difference + TEN_MS / 1000.0 ) *
211 		machine_current->timings.processor_speed
212 		) * speed + 0.5;
213 
214     event_add( last_tstates + tstates, timer_event );
215 
216     start_time = current_time + TEN_MS / 1000.0;
217   }
218 }
219