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 (14 years, 8 months ago) by nigel
Branch: MAIN
CVS Tags: HEAD
Changes since 1.13: +4 -3 lines
Log Message:
Another build fix for changed Prefs methods

File Contents

# User Rev Content
1 nigel 1.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 nigel 1.14 * $Id: Emulator.mm,v 1.13 2008/01/01 09:40:32 gbeauche Exp $
6 nigel 1.1 *
7 gbeauche 1.13 * Basilisk II (C) 1997-2008 Christian Bauer
8 nigel 1.1 *
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 nigel 1.9 #import "adb.h"
34     #import "main.h"
35     #import "prefs.h"
36     #import "timer.h"
37 nigel 1.1
38 nigel 1.14 #undef check // memory.h defines a check macro,
39     // which may clash with an OS X one on 10.1 or 10.2
40 nigel 1.10 #import "cpu_emulation.h"
41 nigel 1.1
42 nigel 1.4 #define DEBUG 0
43 nigel 1.10 #import "debug.h"
44 nigel 1.1
45 nigel 1.9 @implementation Emulator
46    
47 nigel 1.1 // 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 nigel 1.6 #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 nigel 1.1 - (IBAction) Interrupt: (id)sender;
173     {
174 nigel 1.3 WarningSheet (@"Interrupt action not yet supported", win);
175 nigel 1.1 }
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 nigel 1.7 if ( ! running )
195     {
196     running = YES; // Start emulator
197     [self runUpdate];
198     [self Resume: nil];
199     }
200    
201 nigel 1.1 if ( running )
202 nigel 1.7 #ifdef UAE_CPU_HAS_RESET
203     reset680x0();
204     #else
205 nigel 1.1 {
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 nigel 1.6 [screen clear];
212     [screen display];
213 nigel 1.1
214     [emul terminate]; QuitEmuNoExit();
215    
216 nigel 1.6
217     // OK. We have killed & cleaned up. Now, start afresh:
218     #include <sys.h>
219     int argc = 0;
220     char **argv;
221    
222 nigel 1.14 PrefsInit(NULL, argc, argv);
223 nigel 1.6 SysInit();
224    
225 nigel 1.4 emul = [NNThread new];
226 nigel 1.1 [emul perform:@selector(emulThread) of:self];
227     [emul start];
228    
229     if ( display_type != DISPLAY_SCREEN )
230     [redraw resume];
231     }
232 nigel 1.7 #endif
233 nigel 1.1 }
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 nigel 1.2 - (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 nigel 1.1 - (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 nigel 1.3 nil, win);
258 nigel 1.1 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 nigel 1.3 // Make UI threadsafe:
352     [NSThread detachNewThreadSelector:(SEL)"" toTarget:nil withObject:nil];
353 nigel 1.1 //emul = [[NNThread alloc] initWithAutoReleasePool];
354     #endif
355 nigel 1.4 emul = [NNThread new];
356     RTC = [NNTimer new];
357 nigel 1.10 redraw = [[NNTimer alloc] initWithAutoRelPool];
358 nigel 1.4 tick = [NNTimer new];
359     xPRAM = [NNTimer new];
360 nigel 1.1
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 nigel 1.4 NSAutoreleasePool *pool = [NSAutoreleasePool new];
399 nigel 1.1
400 nigel 1.5 if ( ! InitEmulator() )
401     {
402     [redraw suspend]; // Stop the barberpole
403 nigel 1.1
404 nigel 1.5 ErrorSheet(@"Cannot start Emulator", @"", @"Quit", win);
405     }
406 nigel 1.1 else
407     {
408     memcpy(lastXPRAM, XPRAM, XPRAM_SIZE);
409    
410     uaeCreated = YES; // Enable timers to access emulated Mac's memory
411    
412 nigel 1.3 while ( screen == nil ) // If we are still loading from Nib?
413 nigel 1.1 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow: 1.0]];
414    
415 nigel 1.5 [self runUpdate]; // Set the window close gadget to dimpled
416 nigel 1.1
417     Start680x0(); // Start 68k and jump to ROM boot routine
418    
419     puts ("Emulator exited normally");
420     }
421    
422     [pool release];
423 nigel 1.5 QuitEmulator();
424 nigel 1.1 }
425    
426     - (void) RTCinterrupt
427     {
428 gbeauche 1.12 if ( ! uaeCreated )
429     return;
430    
431     WriteMacInt32 (0x20c, TimerDateTime() ); // Update MacOS time
432    
433     SetInterruptFlag(INTFLAG_1HZ);
434     TriggerInterrupt();
435 nigel 1.1 }
436    
437     - (void) redrawScreen
438     {
439     if ( display_type == DISPLAY_SCREEN )
440     {
441 nigel 1.6 NSLog(@"We are in fullscreen mode - why was redrawScreen() called?");
442 nigel 1.1 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 nigel 1.6 // [screen display] or [screen cgDrawInto: ...];
448 nigel 1.1 }
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 nigel 1.2 @end