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

# 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: Emulator.mm,v 1.3 2002/05/30 12:36:17 nigel Exp $
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, clashes with an OS X one?
41 #import <cpu_emulation.h>
42
43 #define DEBUG 0
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
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 WarningSheet (@"Interrupt action not yet supported", win);
153 }
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 emul = [NNThread new];
186 [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 - (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 - (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 nil, win);
218 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 // Make UI threadsafe:
312 [NSThread detachNewThreadSelector:(SEL)"" toTarget:nil withObject:nil];
313 //emul = [[NNThread alloc] initWithAutoReleasePool];
314 #endif
315 emul = [NNThread new];
316 RTC = [NNTimer new];
317 redraw = [NNTimer new];
318 tick = [NNTimer new];
319 xPRAM = [NNTimer new];
320
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 NSAutoreleasePool *pool = [NSAutoreleasePool new];
362
363 InitEmulator();
364
365 if ( RAMBaseHost == NULL || ROMBaseHost == NULL )
366 ErrorSheet(@"Cannot start Emulator",
367 @"Emulator memory not allocated", nil, win);
368 else
369 {
370 memcpy(lastXPRAM, XPRAM, XPRAM_SIZE);
371
372 uaeCreated = YES; // Enable timers to access emulated Mac's memory
373
374 while ( screen == nil ) // If we are still loading from Nib?
375 [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 @end