ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/Emulator.mm
Revision: 1.14
Committed: 2009-10-08T09:20:51Z (15 years, 1 month ago) by nigel
Branch: MAIN
CVS Tags: HEAD
Changes since 1.13: +4 -3 lines
Error occurred while calculating annotation data.
Log Message:
Another build fix for changed Prefs methods

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