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, 5 months 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

# 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.9 * $Id: Emulator.mm,v 1.8 2004/01/12 15:29:24 cebix Exp $
6 nigel 1.1 *
7 cebix 1.8 * Basilisk II (C) 1997-2004 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.9 #undef check // memory.h defines a check macro, clashes with an OS X one?
39 nigel 1.1 #import <cpu_emulation.h>
40    
41 nigel 1.4 #define DEBUG 0
42 nigel 1.1 #import <debug.h>
43    
44 nigel 1.9 @implementation Emulator
45    
46 nigel 1.1 // 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 nigel 1.6 #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 nigel 1.1 - (IBAction) Interrupt: (id)sender;
172     {
173 nigel 1.3 WarningSheet (@"Interrupt action not yet supported", win);
174 nigel 1.1 }
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 nigel 1.7 if ( ! running )
194     {
195     running = YES; // Start emulator
196     [self runUpdate];
197     [self Resume: nil];
198     }
199    
200 nigel 1.1 if ( running )
201 nigel 1.7 #ifdef UAE_CPU_HAS_RESET
202     reset680x0();
203     #else
204 nigel 1.1 {
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 nigel 1.6 [screen clear];
211     [screen display];
212 nigel 1.1
213     [emul terminate]; QuitEmuNoExit();
214    
215 nigel 1.6
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 nigel 1.4 emul = [NNThread new];
225 nigel 1.1 [emul perform:@selector(emulThread) of:self];
226     [emul start];
227    
228     if ( display_type != DISPLAY_SCREEN )
229     [redraw resume];
230     }
231 nigel 1.7 #endif
232 nigel 1.1 }
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 nigel 1.2 - (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 nigel 1.1 - (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 nigel 1.3 nil, win);
257 nigel 1.1 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 nigel 1.3 // Make UI threadsafe:
351     [NSThread detachNewThreadSelector:(SEL)"" toTarget:nil withObject:nil];
352 nigel 1.1 //emul = [[NNThread alloc] initWithAutoReleasePool];
353     #endif
354 nigel 1.4 emul = [NNThread new];
355     RTC = [NNTimer new];
356 nigel 1.9 redraw = [[NNTimer alloc] initWithAutoReleasePool]
357 nigel 1.4 tick = [NNTimer new];
358     xPRAM = [NNTimer new];
359 nigel 1.1
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 nigel 1.4 NSAutoreleasePool *pool = [NSAutoreleasePool new];
398 nigel 1.1
399 nigel 1.5 if ( ! InitEmulator() )
400     {
401     [redraw suspend]; // Stop the barberpole
402 nigel 1.1
403 nigel 1.5 ErrorSheet(@"Cannot start Emulator", @"", @"Quit", win);
404     }
405 nigel 1.1 else
406     {
407     memcpy(lastXPRAM, XPRAM, XPRAM_SIZE);
408    
409     uaeCreated = YES; // Enable timers to access emulated Mac's memory
410    
411 nigel 1.3 while ( screen == nil ) // If we are still loading from Nib?
412 nigel 1.1 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow: 1.0]];
413    
414 nigel 1.5 [self runUpdate]; // Set the window close gadget to dimpled
415 nigel 1.1
416     Start680x0(); // Start 68k and jump to ROM boot routine
417    
418     puts ("Emulator exited normally");
419     }
420    
421     [pool release];
422 nigel 1.5 QuitEmulator();
423 nigel 1.1 }
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 nigel 1.6 NSLog(@"We are in fullscreen mode - why was redrawScreen() called?");
436 nigel 1.1 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 nigel 1.6 // [screen display] or [screen cgDrawInto: ...];
442 nigel 1.1 }
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 nigel 1.2 @end