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

# 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$
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