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