pcsxr-1.9.92
[pcsx_rearmed.git] / macosx / EmuThread.m
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