Commit | Line | Data |
---|---|---|
ef79bbde P |
1 | // |
2 | // EmuThread.m | |
3 | // Pcsx | |
4 | // | |
5 | // Created by Gil Pedersen on Sun Sep 21 2003. | |
6 | // Copyright (c) 2003 __MyCompanyName__. All rights reserved. | |
7 | // | |
8 | ||
9 | #import <ExceptionHandling/NSExceptionHandler.h> | |
10 | #import <AppKit/NSApplication.h> | |
11 | #include <pthread.h> | |
12 | #include <setjmp.h> | |
13 | #import "EmuThread.h" | |
14 | #include "psxcommon.h" | |
15 | #include "plugins.h" | |
16 | #include "misc.h" | |
17 | ||
18 | EmuThread *emuThread; | |
19 | NSString *defrostPath = nil; | |
20 | static int safeEvent; | |
21 | static int paused; | |
22 | static int runbios; | |
23 | ||
24 | static pthread_cond_t eventCond; | |
25 | static pthread_mutex_t eventMutex; | |
26 | ||
27 | #define EMUEVENT_NONE 0 | |
28 | #define EMUEVENT_PAUSE (1<<0) | |
29 | #define EMUEVENT_RESET (1<<1) | |
30 | #define EMUEVENT_STOP (1<<2) | |
31 | ||
32 | @implementation EmuThread | |
33 | ||
34 | - (void)EmuThreadRun:(id)anObject | |
35 | { | |
36 | pool = [[NSAutoreleasePool alloc] init]; | |
37 | ||
38 | [[NSNotificationCenter defaultCenter] addObserver:self | |
39 | selector:@selector(emuWindowDidClose:) | |
40 | name:@"emuWindowDidClose" object:nil]; | |
41 | ||
42 | [[NSNotificationCenter defaultCenter] addObserver:self | |
43 | selector:@selector(emuWindowWantPause:) | |
44 | name:@"emuWindowWantPause" object:nil]; | |
45 | ||
46 | [[NSNotificationCenter defaultCenter] addObserver:self | |
47 | selector:@selector(emuWindowWantResume:) | |
48 | name:@"emuWindowWantResume" object:nil]; | |
49 | ||
50 | // we shouldn't change the priority, since we might depend on subthreads | |
51 | //[NSThread setThreadPriority:1.0-((1.0-[NSThread threadPriority])/4.0)]; | |
52 | ||
53 | // Do processing here | |
54 | if (OpenPlugins() == -1) | |
55 | goto done; | |
56 | ||
57 | setjmp(restartJmp); | |
58 | ||
59 | EmuReset(); | |
60 | ||
61 | int res = CheckCdrom(); | |
62 | if (res == -1) { | |
63 | ClosePlugins(); | |
64 | SysMessage(_("Could not check CD-ROM!\n")); | |
65 | goto done; | |
66 | } | |
67 | ||
68 | LoadCdrom(); | |
69 | ||
70 | if (defrostPath) { | |
71 | LoadState([defrostPath fileSystemRepresentation]); | |
72 | [defrostPath release]; defrostPath = nil; | |
73 | } | |
74 | ||
75 | psxCpu->Execute(); | |
76 | ||
77 | done: | |
78 | [pool release]; pool = nil; | |
79 | emuThread = nil; | |
80 | ||
81 | return; | |
82 | } | |
83 | ||
84 | - (void)EmuThreadRunBios:(id)anObject | |
85 | { | |
86 | pool = [[NSAutoreleasePool alloc] init]; | |
87 | ||
88 | [[NSNotificationCenter defaultCenter] addObserver:self | |
89 | selector:@selector(emuWindowDidClose:) | |
90 | name:@"emuWindowDidClose" object:nil]; | |
91 | ||
92 | [[NSNotificationCenter defaultCenter] addObserver:self | |
93 | selector:@selector(emuWindowWantPause:) | |
94 | name:@"emuWindowWantPause" object:nil]; | |
95 | ||
96 | [[NSNotificationCenter defaultCenter] addObserver:self | |
97 | selector:@selector(emuWindowWantResume:) | |
98 | name:@"emuWindowWantResume" object:nil]; | |
99 | ||
100 | // we shouldn't change the priority, since we might depend on subthreads | |
101 | //[NSThread setThreadPriority:1.0-((1.0-[NSThread threadPriority])/4.0)]; | |
102 | ||
103 | // Do processing here | |
104 | if (OpenPlugins() == -1) | |
105 | goto done; | |
106 | ||
107 | EmuReset(); | |
108 | ||
109 | psxCpu->Execute(); | |
110 | ||
111 | done: | |
112 | [pool release]; pool = nil; | |
113 | emuThread = nil; | |
114 | ||
115 | return; | |
116 | } | |
117 | ||
118 | - (void)dealloc | |
119 | { | |
120 | // remove all registered observers | |
121 | [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:nil]; | |
122 | ||
123 | if (pool) | |
124 | [pool release]; | |
125 | ||
126 | [super dealloc]; | |
127 | } | |
128 | ||
129 | - (void)emuWindowDidClose:(NSNotification *)aNotification | |
130 | { | |
131 | [EmuThread stop]; | |
132 | } | |
133 | ||
134 | - (void)emuWindowWantPause:(NSNotification *)aNotification | |
135 | { | |
136 | wasPaused = [EmuThread pause]; | |
137 | } | |
138 | ||
139 | - (void)emuWindowWantResume:(NSNotification *)aNotification | |
140 | { | |
141 | if (!wasPaused) { | |
142 | [EmuThread resume]; | |
143 | } | |
144 | wasPaused = NO; | |
145 | } | |
146 | ||
147 | /* called periodically from the emulation thread */ | |
148 | - (void)handleEvents | |
149 | { | |
150 | /* only do a trylock here, since we're not interested in blocking, | |
151 | and we can just handle events next time round */ | |
152 | if (pthread_mutex_trylock(&eventMutex) == 0) { | |
153 | while (safeEvent) { | |
154 | if (safeEvent & EMUEVENT_STOP) { | |
155 | /* signify that the emulation has stopped */ | |
156 | [emuThread autorelease]; | |
157 | emuThread = nil; | |
158 | paused = NO; | |
159 | ||
160 | /* better unlock the mutex before killing ourself */ | |
161 | pthread_mutex_unlock(&eventMutex); | |
162 | ||
163 | ClosePlugins(); | |
164 | SysClose(); | |
165 | ||
166 | //[[NSThread currentThread] autorelease]; | |
167 | [NSThread exit]; | |
168 | return; | |
169 | } | |
170 | ||
171 | if (safeEvent & EMUEVENT_RESET) { | |
172 | #if 0 | |
173 | /* signify that the emulation has stopped */ | |
174 | [emuThread autorelease]; | |
175 | emuThread = nil; | |
176 | ||
177 | /* better unlock the mutex before killing ourself */ | |
178 | pthread_mutex_unlock(&eventMutex); | |
179 | ||
180 | ClosePlugins(); | |
181 | ||
182 | // start a new emulation thread | |
183 | [EmuThread run]; | |
184 | ||
185 | //[[NSThread currentThread] autorelease]; | |
186 | [NSThread exit]; | |
187 | return; | |
188 | #else | |
189 | safeEvent &= ~EMUEVENT_RESET; | |
190 | pthread_mutex_unlock(&eventMutex); | |
191 | ||
192 | longjmp(restartJmp, 0); | |
193 | #endif | |
194 | } | |
195 | ||
196 | if (safeEvent & EMUEVENT_PAUSE) { | |
197 | paused = 2; | |
198 | /* wait until we're signalled */ | |
199 | pthread_cond_wait(&eventCond, &eventMutex); | |
200 | } | |
201 | } | |
202 | pthread_mutex_unlock(&eventMutex); | |
203 | } | |
204 | } | |
205 | ||
206 | + (void)run | |
207 | { | |
208 | int err; | |
209 | ||
210 | if (emuThread) { | |
211 | [EmuThread resume]; | |
212 | return; | |
213 | } | |
214 | ||
215 | if (pthread_mutex_lock(&eventMutex) != 0) { | |
216 | err = pthread_cond_init(&eventCond, NULL); | |
217 | if (err) return; | |
218 | ||
219 | err = pthread_mutex_init(&eventMutex, NULL); | |
220 | if (err) return; | |
221 | ||
222 | pthread_mutex_lock(&eventMutex); | |
223 | } | |
224 | ||
225 | safeEvent = EMUEVENT_NONE; | |
226 | paused = NO; | |
227 | runbios = NO; | |
228 | ||
229 | if (SysInit() != 0) { | |
230 | pthread_mutex_unlock(&eventMutex); | |
231 | return; | |
232 | } | |
233 | ||
234 | emuThread = [[EmuThread alloc] init]; | |
235 | ||
236 | [NSThread detachNewThreadSelector:@selector(EmuThreadRun:) | |
237 | toTarget:emuThread withObject:nil]; | |
238 | ||
239 | pthread_mutex_unlock(&eventMutex); | |
240 | } | |
241 | ||
242 | + (void)runBios | |
243 | { | |
244 | int err; | |
245 | ||
246 | if (emuThread) { | |
247 | [EmuThread resume]; | |
248 | return; | |
249 | } | |
250 | ||
251 | if (pthread_mutex_lock(&eventMutex) != 0) { | |
252 | err = pthread_cond_init(&eventCond, NULL); | |
253 | if (err) return; | |
254 | ||
255 | err = pthread_mutex_init(&eventMutex, NULL); | |
256 | if (err) return; | |
257 | ||
258 | pthread_mutex_lock(&eventMutex); | |
259 | } | |
260 | ||
261 | safeEvent = EMUEVENT_NONE; | |
262 | paused = NO; | |
263 | runbios = YES; | |
264 | ||
265 | if (SysInit() != 0) { | |
266 | pthread_mutex_unlock(&eventMutex); | |
267 | return; | |
268 | } | |
269 | ||
270 | emuThread = [[EmuThread alloc] init]; | |
271 | ||
272 | [NSThread detachNewThreadSelector:@selector(EmuThreadRunBios:) | |
273 | toTarget:emuThread withObject:nil]; | |
274 | ||
275 | pthread_mutex_unlock(&eventMutex); | |
276 | } | |
277 | ||
278 | + (void)stop | |
279 | { | |
280 | pthread_mutex_lock(&eventMutex); | |
281 | safeEvent = EMUEVENT_STOP; | |
282 | pthread_mutex_unlock(&eventMutex); | |
283 | ||
284 | // wake it if it's sleeping | |
285 | pthread_cond_broadcast(&eventCond); | |
286 | } | |
287 | ||
288 | + (BOOL)pause | |
289 | { | |
290 | if (paused || ![EmuThread active]) | |
291 | return YES; | |
292 | ||
293 | pthread_mutex_lock(&eventMutex); | |
294 | safeEvent |= EMUEVENT_PAUSE; | |
295 | paused = 1; | |
296 | pthread_mutex_unlock(&eventMutex); | |
297 | ||
298 | pthread_cond_broadcast(&eventCond); | |
299 | ||
300 | return NO; | |
301 | } | |
302 | ||
303 | + (BOOL)pauseSafe | |
304 | { | |
305 | if ((paused == 2) || ![EmuThread active]) | |
306 | return YES; | |
307 | ||
308 | [EmuThread pause]; | |
309 | while ([EmuThread isPaused] != 2) [NSThread sleepUntilDate:[[NSDate date] addTimeInterval:0.05]]; | |
310 | ||
311 | return NO; | |
312 | } | |
313 | ||
314 | + (void)resume | |
315 | { | |
316 | if (!paused || ![EmuThread active]) | |
317 | return; | |
318 | ||
319 | pthread_mutex_lock(&eventMutex); | |
320 | ||
321 | safeEvent &= ~EMUEVENT_PAUSE; | |
322 | paused = NO; | |
323 | pthread_mutex_unlock(&eventMutex); | |
324 | ||
325 | pthread_cond_broadcast(&eventCond); | |
326 | } | |
327 | ||
328 | + (void)reset | |
329 | { | |
330 | pthread_mutex_lock(&eventMutex); | |
331 | safeEvent = EMUEVENT_RESET; | |
332 | pthread_mutex_unlock(&eventMutex); | |
333 | ||
334 | pthread_cond_broadcast(&eventCond); | |
335 | } | |
336 | ||
337 | // must only be called from within the emulation thread!!! | |
338 | + (void)resetNow | |
339 | { | |
340 | /* signify that the emulation has stopped */ | |
341 | [emuThread autorelease]; | |
342 | emuThread = nil; | |
343 | ||
344 | ClosePlugins(); | |
345 | ||
346 | // start a new emulation thread | |
347 | [EmuThread run]; | |
348 | ||
349 | //[[NSThread currentThread] autorelease]; | |
350 | [NSThread exit]; | |
351 | return; | |
352 | } | |
353 | ||
354 | + (BOOL)isPaused | |
355 | { | |
356 | return paused; | |
357 | } | |
358 | ||
359 | + (BOOL)isRunBios | |
360 | { | |
361 | return runbios; | |
362 | } | |
363 | ||
364 | + (BOOL)active | |
365 | { | |
366 | return emuThread ? YES : NO; | |
367 | } | |
368 | ||
369 | + (void)freezeAt:(NSString *)path which:(int)num | |
370 | { | |
371 | BOOL emuWasPaused = [EmuThread pauseSafe]; | |
372 | char Text[256]; | |
373 | ||
374 | GPU_freeze(2, (GPUFreeze_t *)&num); | |
375 | int ret = SaveState([path fileSystemRepresentation]); | |
376 | if (ret == 0) sprintf (Text, _("*PCSX*: Saved State %d"), num+1); | |
377 | else sprintf (Text, _("*PCSX*: Error Saving State %d"), num+1); | |
378 | GPU_displayText(Text); | |
379 | ||
380 | if (!emuWasPaused) { | |
381 | [EmuThread resume]; | |
382 | } | |
383 | } | |
384 | ||
385 | + (BOOL)defrostAt:(NSString *)path | |
386 | { | |
387 | const char *cPath = [path fileSystemRepresentation]; | |
388 | if (CheckState(cPath) != 0) | |
389 | return NO; | |
390 | ||
391 | defrostPath = [path retain]; | |
392 | [EmuThread reset]; | |
393 | ||
394 | GPU_displayText(_("*PCSX*: Loaded State")); | |
395 | return YES; | |
396 | } | |
397 | ||
398 | @end |