ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/Emulator.mm
Revision: 1.4
Committed: 2002-12-18T11:50:12Z (21 years, 11 months ago) by nigel
Branch: MAIN
CVS Tags: nigel-build-12
Changes since 1.3: +10 -12 lines
Log Message:
[[blah alloc] init] becomes [blah new]

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