ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/Emulator.mm
Revision: 1.9
Committed: 2004-05-25T05:26:41Z (20 years ago) by nigel
Branch: MAIN
Changes since 1.8: +9 -9 lines
Log Message:
Force an autorelease pool around the redraw thread.
Requires an updated NNThread class

File Contents

# Content
1 /*
2 * Emulator.mm - Class whose actions are attached to GUI widgets in a window,
3 * used to control a single Basilisk II emulated Macintosh.
4 *
5 * $Id: Emulator.mm,v 1.8 2004/01/12 15:29:24 cebix Exp $
6 *
7 * Basilisk II (C) 1997-2004 Christian Bauer
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #import "Emulator.h"
25 #import "EmulatorView.h"
26
27 #import "sysdeps.h" // Types used in Basilisk C++ code
28
29 #import "main_macosx.h" // Prototypes for QuitEmuNoExit() and InitEmulator()
30 #import "misc_macosx.h" // Some other prototypes
31 #import "video_macosx.h" // Some window/view globals
32
33 #import "adb.h"
34 #import "main.h"
35 #import "prefs.h"
36 #import "timer.h"
37
38 #undef check // memory.h defines a check macro, clashes with an OS X one?
39 #import <cpu_emulation.h>
40
41 #define DEBUG 0
42 #import <debug.h>
43
44 @implementation Emulator
45
46 // NSWindow method, which is invoked via delegation
47
48 - (BOOL) windowShouldClose: (id)sender
49 {
50 if ( uaeCreated )
51 {
52 NSLog(@"windowShouldClose returning NO");
53 return NO; // Should initiate poweroff and return NSTerminateLater ?
54 }
55
56 NSLog(@"windowShouldClose returning YES");
57 return YES;
58 }
59
60 // Default methods
61
62 - (Emulator *) init
63 {
64 int frameSkip;
65
66 self = [super init];
67
68 running = NO; // Save churn when application loads
69 // running = YES;
70 uaeCreated = NO;
71
72 frameSkip = PrefsFindInt32("frameskip");
73 if ( frameSkip )
74 redrawDelay = frameSkip / 60.0;
75 else
76 redrawDelay = 0.0;
77
78 // We do this so that we can work out if we are in full screen mode:
79 parse_screen_prefs(PrefsFindString("screen"));
80
81 [self createThreads];
82
83 return self;
84 }
85
86 - (void) awakeFromNib
87 {
88 the_win = win; // Set global for access by Basilisk C++ code
89
90
91 [win setDelegate: self]; // Enable windowShouldClose calling
92
93 // Try to speed up everything
94 //[win setHasShadow: NO]; // This causes view & window to now be drawn correctly
95 [win useOptimizedDrawing: YES];
96
97 [win makeKeyAndOrderFront:self];
98
99 if ( redrawDelay )
100 [speed setFloatValue: 1.0 / redrawDelay];
101 else
102 [speed setFloatValue: 60.0];
103
104
105 if ( runOrPause == nil )
106 NSLog(@"%s - runOrPause button pointer is nil!", __PRETTY_FUNCTION__);
107
108 [self runUpdate];
109 }
110
111
112 // Helpers which other classes use to access our private stuff
113
114 - (BOOL) isRunning { return running; }
115 - (BOOL) uaeCreated { return uaeCreated; }
116 - (EmulatorView *) screen { return screen; }
117 - (NSSlider *) speed { return speed; }
118 - (NSWindow *) window { return win; }
119
120
121 // Update some UI elements
122
123 - (void) runUpdate
124 {
125 if ( running )
126 [runOrPause setState: NSOnState]; // Running. Change button label to 'Pause'
127 else
128 [runOrPause setState: NSOffState]; // Paused. Change button label to 'Run'
129
130 [win setDocumentEdited: uaeCreated]; // Set the little dimple in the close button
131 }
132
133
134 // Methods invoked by buttons & menu items
135
136 - (IBAction) Benchmark: (id)sender;
137 {
138 BOOL wasRunning = running;
139
140 if ( running )
141 [self Suspend: self];
142 [screen benchmark];
143 if ( wasRunning )
144 [self Resume: self];
145 }
146
147 #ifdef NIGEL
148 - (IBAction) EjectCD: (id)sender;
149 {
150 NSString *path;
151 const char *cdrom = PrefsFindString("cdrom");
152
153 if ( cdrom )
154 {
155 #include <sys/param.h>
156 #define KERNEL
157 #include <sys/mount.h>
158
159 struct statfs buf;
160 if ( fsstat(path, &buf) < 0 )
161 return;
162
163 path = [NSString stringWithCString: cdrom];
164
165 [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: path];
166 // [path release];
167 }
168 }
169 #endif
170
171 - (IBAction) Interrupt: (id)sender;
172 {
173 WarningSheet (@"Interrupt action not yet supported", win);
174 }
175
176 - (IBAction) PowerKey: (id)sender;
177 {
178 if ( uaeCreated ) // If Mac has started
179 {
180 ADBKeyDown(0x7f); // Send power key, which is also
181 ADBKeyUp(0x7f); // called ADB_RESET or ADB_POWER
182 }
183 else
184 {
185 running = YES; // Start emulator
186 [self runUpdate];
187 [self Resume: nil];
188 }
189 }
190
191 - (IBAction) Restart: (id)sender
192 {
193 if ( ! running )
194 {
195 running = YES; // Start emulator
196 [self runUpdate];
197 [self Resume: nil];
198 }
199
200 if ( running )
201 #ifdef UAE_CPU_HAS_RESET
202 reset680x0();
203 #else
204 {
205 uaeCreated = NO;
206 [redraw suspend];
207 NSLog (@"%s - uae_cpu reset not yet supported, will try to fake it",
208 __PRETTY_FUNCTION__);
209
210 [screen clear];
211 [screen display];
212
213 [emul terminate]; QuitEmuNoExit();
214
215
216 // OK. We have killed & cleaned up. Now, start afresh:
217 #include <sys.h>
218 int argc = 0;
219 char **argv;
220
221 PrefsInit(argc, argv);
222 SysInit();
223
224 emul = [NNThread new];
225 [emul perform:@selector(emulThread) of:self];
226 [emul start];
227
228 if ( display_type != DISPLAY_SCREEN )
229 [redraw resume];
230 }
231 #endif
232 }
233
234 - (IBAction) Resume: (id)sender
235 {
236 [RTC resume];
237 [emul resume];
238 if ( display_type != DISPLAY_SCREEN )
239 [redraw resume];
240 [tick resume];
241 [xPRAM resume];
242 }
243
244 - (IBAction) ScreenHideShow: (NSButton *)sender;
245 {
246 WarningSheet(@"Nigel doesn't know how to shrink or grow this window",
247 @"Maybe you can grab the source code and have a go yourself?",
248 nil, win);
249 }
250
251 - (IBAction) Snapshot: (id) sender
252 {
253 if ( screen == nil || uaeCreated == NO )
254 WarningSheet(@"The emulator has not yet started.",
255 @"There is no screen output to snapshot",
256 nil, win);
257 else
258 {
259 NSData *TIFFdata;
260
261 [self Suspend: self];
262
263 TIFFdata = [screen TIFFrep];
264 if ( TIFFdata == nil )
265 NSLog(@"%s - Unable to convert Basilisk screen to a TIFF representation",
266 __PRETTY_FUNCTION__);
267 else
268 {
269 NSSavePanel *sp = [NSSavePanel savePanel];
270
271 [sp setRequiredFileType:@"tiff"];
272
273 if ( [sp runModalForDirectory:NSHomeDirectory()
274 file:@"B2-screen-snapshot.tiff"] == NSOKButton )
275 if ( ! [TIFFdata writeToFile:[sp filename] atomically:YES] )
276 NSLog(@"%s - Could not write TIFF data to file @%",
277 __PRETTY_FUNCTION__, [sp filename]);
278
279 }
280 if ( running )
281 [self Resume: self];
282 }
283 }
284
285 - (IBAction) SpeedChange: (NSSlider *)sender
286 {
287 float frequency = [sender floatValue];
288
289 [redraw suspend];
290
291 if ( frequency == 0.0 )
292 redrawDelay = 0.0;
293 else
294 {
295 frequencyToTickDelay(frequency);
296
297 redrawDelay = 1.0 / frequency;
298
299 [redraw changeIntervalTo: (int)(redrawDelay * 1e6)
300 units: NNmicroSeconds];
301 if ( running && display_type != DISPLAY_SCREEN )
302 [redraw resume];
303 }
304 }
305
306 - (IBAction) Suspend: (id)sender
307 {
308 [RTC suspend];
309 [emul suspend];
310 [redraw suspend];
311 [tick suspend];
312 [xPRAM suspend];
313 }
314
315 - (IBAction) ToggleState: (NSButton *)sender
316 {
317 running = [sender state]; // State of the toggled NSButton
318 if ( running )
319 [self Resume: nil];
320 else
321 [self Suspend: nil];
322 }
323
324 - (IBAction) Terminate: (id)sender;
325 {
326 [self exitThreads];
327 [win performClose: self];
328 }
329
330 #include <xpram.h>
331
332 #define XPRAM_SIZE 256
333
334 uint8 lastXPRAM[XPRAM_SIZE]; // Copy of PRAM
335
336 - (IBAction) ZapPRAM: (id)sender;
337 {
338 memset(XPRAM, 0, XPRAM_SIZE);
339 memset(lastXPRAM, 0, XPRAM_SIZE);
340 ZapPRAM();
341 }
342
343 //
344 // Threads, Timers and stuff to manage them:
345 //
346
347 - (void) createThreads
348 {
349 #ifdef USE_PTHREADS
350 // Make UI threadsafe:
351 [NSThread detachNewThreadSelector:(SEL)"" toTarget:nil withObject:nil];
352 //emul = [[NNThread alloc] initWithAutoReleasePool];
353 #endif
354 emul = [NNThread new];
355 RTC = [NNTimer new];
356 redraw = [[NNTimer alloc] initWithAutoReleasePool]
357 tick = [NNTimer new];
358 xPRAM = [NNTimer new];
359
360 [emul perform:@selector(emulThread) of:self];
361 [RTC repeat:@selector(RTCinterrupt) of:self
362 every:1
363 units:NNseconds];
364 [redraw repeat:@selector(redrawScreen) of:self
365 every:(int)(1000*redrawDelay)
366 units:NNmilliSeconds];
367 [tick repeat:@selector(tickInterrupt) of:self
368 every:16625
369 units:NNmicroSeconds];
370 [xPRAM repeat:@selector(xPRAMbackup) of:self
371 every:60
372 units:NNseconds];
373
374 if ( running ) // Start emulator, then threads in most economical order
375 {
376 [emul start];
377 [xPRAM start];
378 [RTC start];
379 if ( display_type != DISPLAY_SCREEN )
380 [redraw start];
381 [tick start];
382 }
383 }
384
385 - (void) exitThreads
386 {
387 running = NO;
388 [emul terminate]; [emul release]; emul = nil;
389 [tick invalidate]; [tick release]; tick = nil;
390 [redraw invalidate]; [redraw release]; redraw = nil;
391 [RTC invalidate]; [RTC release]; RTC = nil;
392 [xPRAM invalidate]; [xPRAM release]; xPRAM = nil;
393 }
394
395 - (void) emulThread
396 {
397 NSAutoreleasePool *pool = [NSAutoreleasePool new];
398
399 if ( ! InitEmulator() )
400 {
401 [redraw suspend]; // Stop the barberpole
402
403 ErrorSheet(@"Cannot start Emulator", @"", @"Quit", win);
404 }
405 else
406 {
407 memcpy(lastXPRAM, XPRAM, XPRAM_SIZE);
408
409 uaeCreated = YES; // Enable timers to access emulated Mac's memory
410
411 while ( screen == nil ) // If we are still loading from Nib?
412 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow: 1.0]];
413
414 [self runUpdate]; // Set the window close gadget to dimpled
415
416 Start680x0(); // Start 68k and jump to ROM boot routine
417
418 puts ("Emulator exited normally");
419 }
420
421 [pool release];
422 QuitEmulator();
423 }
424
425 - (void) RTCinterrupt
426 {
427 if ( uaeCreated )
428 WriteMacInt32 (0x20c, TimerDateTime() ); // Update MacOS time
429 }
430
431 - (void) redrawScreen
432 {
433 if ( display_type == DISPLAY_SCREEN )
434 {
435 NSLog(@"We are in fullscreen mode - why was redrawScreen() called?");
436 return;
437 }
438 [barberPole animate:self]; // wobble the pole
439 [screen setNeedsDisplay: YES]; // redisplay next time through runLoop
440 // Or, use a direct method. e.g.
441 // [screen display] or [screen cgDrawInto: ...];
442 }
443
444 #include <main.h> // For #define INTFLAG_60HZ
445 #include <rom_patches.h> // For ROMVersion
446 #include "macos_util_macosx.h" // For HasMacStarted()
447
448 - (void) tickInterrupt
449 {
450 if ( ROMVersion != ROM_VERSION_CLASSIC || HasMacStarted() )
451 {
452 SetInterruptFlag (INTFLAG_60HZ);
453 TriggerInterrupt ();
454 }
455 }
456
457 - (void) xPRAMbackup
458 {
459 if ( uaeCreated &&
460 memcmp(lastXPRAM, XPRAM, XPRAM_SIZE) ) // if PRAM changed from copy
461 {
462 memcpy (lastXPRAM, XPRAM, XPRAM_SIZE); // re-copy
463 SaveXPRAM (); // and save to disk
464 }
465 }
466
467 @end