ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/Emulator.mm
Revision: 1.1
Committed: 2002-03-16T04:00:09Z (22 years, 3 months ago) by nigel
Branch: MAIN
CVS Tags: nigel-build-10
Log Message:
Initial revision of Mac OS X port code. Uses Objective-C++. Needs Mac OS 10.1

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     * $Id$
6     *
7     * Basilisk II (C) 1997-2002 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     @implementation Emulator
28    
29     #import "sysdeps.h" // Types used in Basilisk C++ code
30    
31     #import "main_macosx.h" // Prototypes for QuitEmuNoExit() and InitEmulator()
32     #import "misc_macosx.h" // Some other prototypes
33     #import "video_macosx.h" // Some window/view globals
34    
35     #import <adb.h>
36     #import <main.h>
37     #import <prefs.h>
38     #import <timer.h>
39    
40     #undef check() // memory.h defines a check macro, which clashes with an OS X one?
41     #import <cpu_emulation.h>
42    
43     #define DEBUG 1
44     #import <debug.h>
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 center];
98     [win makeKeyAndOrderFront:self];
99    
100     // [self resizeWinToWidth:x Height:y];
101    
102     if ( redrawDelay )
103     [speed setFloatValue: 1.0 / redrawDelay];
104     else
105     [speed setFloatValue: 60.0];
106    
107    
108     if ( runOrPause == nil )
109     NSLog(@"%s - runOrPause button pointer is nil!", __PRETTY_FUNCTION__);
110    
111     [self runUpdate];
112     }
113    
114    
115     // Helpers which other classes use to access our private stuff
116    
117     - (BOOL) isRunning { return running; }
118     - (BOOL) uaeCreated { return uaeCreated; }
119     - (EmulatorView *) screen { return screen; }
120     - (NSSlider *) speed { return speed; }
121     - (NSWindow *) window { return win; }
122    
123     //#define DEBUG 1
124     //#include <debug.h>
125    
126     // Update some UI elements
127    
128     - (void) runUpdate
129     {
130     if ( running )
131     [runOrPause setState: NSOnState]; // Running. Change button label to 'Pause'
132     else
133     [runOrPause setState: NSOffState]; // Paused. Change button label to 'Run'
134    
135     [win setDocumentEdited: uaeCreated]; // Set the little dimple in the close button
136     }
137    
138    
139     // Methods invoked by buttons & menu items
140    
141     - (IBAction) Benchmark: (id)sender;
142     {
143     BOOL wasRunning = running;
144    
145     if ( running )
146     [self Suspend: self];
147     [screen benchmark];
148     if ( wasRunning )
149     [self Resume: self];
150     }
151    
152     - (IBAction) Interrupt: (id)sender;
153     {
154     WarningSheet (@"Interrupt action not yet supported", @"", @"OK", win);
155     }
156    
157     - (IBAction) PowerKey: (id)sender;
158     {
159     if ( uaeCreated ) // If Mac has started
160     {
161     ADBKeyDown(0x7f); // Send power key, which is also
162     ADBKeyUp(0x7f); // called ADB_RESET or ADB_POWER
163     }
164     else
165     {
166     running = YES; // Start emulator
167     [self runUpdate];
168     [self Resume: nil];
169     }
170     }
171    
172     - (IBAction) Restart: (id)sender
173     {
174     if ( running )
175     // reset680x0();
176     {
177     uaeCreated = NO;
178     [redraw suspend];
179     NSLog (@"%s - uae_cpu reset not yet supported, will try to fake it",
180     __PRETTY_FUNCTION__);
181    
182     // [screen blacken];
183     [screen setNeedsDisplay: YES];
184    
185     [emul terminate]; QuitEmuNoExit();
186    
187     emul = [[NNThread alloc] init];
188     [emul perform:@selector(emulThread) of:self];
189     [emul start];
190    
191     if ( display_type != DISPLAY_SCREEN )
192     [redraw resume];
193     uaeCreated = YES;
194     }
195     }
196    
197     - (IBAction) Resume: (id)sender
198     {
199     [RTC resume];
200     [emul resume];
201     if ( display_type != DISPLAY_SCREEN )
202     [redraw resume];
203     [tick resume];
204     [xPRAM resume];
205     }
206    
207     - (IBAction) Snapshot: (id) sender
208     {
209     if ( screen == nil || uaeCreated == NO )
210     WarningSheet(@"The emulator has not yet started.",
211     @"There is no screen output to snapshot",
212     @"OK", win);
213     else
214     {
215     NSData *TIFFdata;
216    
217     [self Suspend: self];
218    
219     TIFFdata = [screen TIFFrep];
220     if ( TIFFdata == nil )
221     NSLog(@"%s - Unable to convert Basilisk screen to a TIFF representation",
222     __PRETTY_FUNCTION__);
223     else
224     {
225     NSSavePanel *sp = [NSSavePanel savePanel];
226    
227     [sp setRequiredFileType:@"tiff"];
228    
229     if ( [sp runModalForDirectory:NSHomeDirectory()
230     file:@"B2-screen-snapshot.tiff"] == NSOKButton )
231     if ( ! [TIFFdata writeToFile:[sp filename] atomically:YES] )
232     NSLog(@"%s - Could not write TIFF data to file @%",
233     __PRETTY_FUNCTION__, [sp filename]);
234    
235     }
236     if ( running )
237     [self Resume: self];
238     }
239     }
240    
241     - (IBAction) SpeedChange: (NSSlider *)sender
242     {
243     float frequency = [sender floatValue];
244    
245     [redraw suspend];
246    
247     if ( frequency == 0.0 )
248     redrawDelay = 0.0;
249     else
250     {
251     frequencyToTickDelay(frequency);
252    
253     redrawDelay = 1.0 / frequency;
254    
255     [redraw changeIntervalTo: (int)(redrawDelay * 1e6)
256     units: NNmicroSeconds];
257     if ( running && display_type != DISPLAY_SCREEN )
258     [redraw resume];
259     }
260     }
261    
262     - (IBAction) Suspend: (id)sender
263     {
264     [RTC suspend];
265     [emul suspend];
266     [redraw suspend];
267     [tick suspend];
268     [xPRAM suspend];
269     }
270    
271     - (IBAction) ToggleState: (NSButton *)sender
272     {
273     running = [sender state]; // State of the toggled NSButton
274     if ( running )
275     [self Resume: nil];
276     else
277     [self Suspend: nil];
278     }
279    
280     - (IBAction) Terminate: (id)sender;
281     {
282     [self exitThreads];
283     [win performClose: self];
284     }
285    
286     #include <xpram.h>
287    
288     #define XPRAM_SIZE 256
289    
290     uint8 lastXPRAM[XPRAM_SIZE]; // Copy of PRAM
291    
292     - (IBAction) ZapPRAM: (id)sender;
293     {
294     memset(XPRAM, 0, XPRAM_SIZE);
295     memset(lastXPRAM, 0, XPRAM_SIZE);
296     ZapPRAM();
297     }
298    
299     //
300     // Threads, Timers and stuff to manage them:
301     //
302    
303     - (void) createThreads
304     {
305     #ifdef USE_PTHREADS
306     [NSThread detachNewThreadSelector:(SEL)"" toTarget:nil withObject:nil]; // Make UI threadsafe
307     //emul = [[NNThread alloc] initWithAutoReleasePool];
308     #endif
309     emul = [[NNThread alloc] init];
310     RTC = [[NNTimer alloc] init];
311     redraw = [[NNTimer alloc] init];
312     tick = [[NNTimer alloc] init];
313     xPRAM = [[NNTimer alloc] init];
314    
315     [emul perform:@selector(emulThread) of:self];
316     [RTC repeat:@selector(RTCinterrupt) of:self
317     every:1
318     units:NNseconds];
319     [redraw repeat:@selector(redrawScreen) of:self
320     every:(int)(1000*redrawDelay)
321     units:NNmilliSeconds];
322     [tick repeat:@selector(tickInterrupt) of:self
323     every:16625
324     units:NNmicroSeconds];
325     [xPRAM repeat:@selector(xPRAMbackup) of:self
326     every:60
327     units:NNseconds];
328    
329     if ( running ) // Start emulator, then threads in most economical order
330     {
331     [emul start];
332     [xPRAM start];
333     [RTC start];
334     if ( display_type != DISPLAY_SCREEN )
335     [redraw start];
336     [tick start];
337     }
338     }
339    
340     - (void) exitThreads
341     {
342     running = NO;
343     [emul terminate]; [emul release]; emul = nil;
344     [tick invalidate]; [tick release]; tick = nil;
345     [redraw invalidate]; [redraw release]; redraw = nil;
346     [RTC invalidate]; [RTC release]; RTC = nil;
347     [xPRAM invalidate]; [xPRAM release]; xPRAM = nil;
348     if ( uaeCreated )
349     QuitEmuNoExit();
350     }
351    
352     - (void) emulThread
353     {
354     extern uint8 *RAMBaseHost, *ROMBaseHost;
355     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
356    
357     // [screen allocBitmap]; // Do this first, because InitEmulator() calls VideoInit(), which needs the_bitmap.
358    
359     InitEmulator();
360    
361     if ( RAMBaseHost == NULL || ROMBaseHost == NULL )
362     ErrorSheet(@"Cannot start Emulator.",
363     @"Emulator memory not allocated", @"OK", win);
364     else
365     {
366     memcpy(lastXPRAM, XPRAM, XPRAM_SIZE);
367    
368     uaeCreated = YES; // Enable timers to access emulated Mac's memory
369    
370     while ( screen == nil ) // If init sets running, but we are still loading from Nib?
371     [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow: 1.0]];
372    
373     // [screen readyToDraw];
374     [self runUpdate];
375    
376     Start680x0(); // Start 68k and jump to ROM boot routine
377    
378     puts ("Emulator exited normally");
379     }
380    
381     running = NO;
382     uaeCreated = NO;
383     [self runUpdate]; // Update button & dimple
384     [pool release];
385     [self exitThreads];
386     }
387    
388     - (void) RTCinterrupt
389     {
390     if ( uaeCreated )
391     WriteMacInt32 (0x20c, TimerDateTime() ); // Update MacOS time
392     }
393    
394     - (void) redrawScreen
395     {
396     if ( display_type == DISPLAY_SCREEN )
397     {
398     NSLog(@"Why was redrawScreen() called?");
399     return;
400     }
401     [barberPole animate:self]; // wobble the pole
402     [screen setNeedsDisplay: YES]; // redisplay next time through runLoop
403     // Or, use a direct method. e.g.
404     // [screen cgDrawInto: ...];
405     }
406    
407     #include <main.h> // For #define INTFLAG_60HZ
408     #include <rom_patches.h> // For ROMVersion
409     #include "macos_util_macosx.h" // For HasMacStarted()
410    
411     - (void) tickInterrupt
412     {
413     if ( ROMVersion != ROM_VERSION_CLASSIC || HasMacStarted() )
414     {
415     SetInterruptFlag (INTFLAG_60HZ);
416     TriggerInterrupt ();
417     }
418     }
419    
420     - (void) xPRAMbackup
421     {
422     if ( uaeCreated &&
423     memcmp(lastXPRAM, XPRAM, XPRAM_SIZE) ) // if PRAM changed from copy
424     {
425     memcpy (lastXPRAM, XPRAM, XPRAM_SIZE); // re-copy
426     SaveXPRAM (); // and save to disk
427     }
428     }
429    
430     @end