ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/main_macosx.mm
Revision: 1.13
Committed: 2005-01-30T21:42:13Z (19 years, 4 months ago) by gbeauche
Branch: MAIN
Changes since 1.12: +2 -2 lines
Log Message:
Happy New Year!

File Contents

# Content
1 /*
2 * $Id: main_macosx.mm,v 1.12 2004/01/27 00:18:24 nigel Exp $
3 *
4 * main_macosx.mm - Startup code for MacOS X
5 * Based (in a small way) on the default main.m,
6 and on Basilisk's main_unix.cpp
7 *
8 * Basilisk II (C) 1997-2005 Christian Bauer
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 #define PTHREADS // Why is this here?
25 #include "sysdeps.h"
26
27 #ifdef HAVE_PTHREADS
28 # include <pthread.h>
29 #endif
30
31 #if REAL_ADDRESSING || DIRECT_ADDRESSING
32 # include <sys/mman.h>
33 #endif
34
35 #include <string>
36 using std::string;
37
38 #include "cpu_emulation.h"
39 #include "macos_util_macosx.h"
40 #include "main.h"
41 #include "prefs.h"
42 #include "prefs_editor.h"
43 #include "rom_patches.h"
44 #include "sigsegv.h"
45 #include "sys.h"
46 #include "user_strings.h"
47 #include "version.h"
48 #include "video.h"
49 #include "vm_alloc.h"
50 #include "xpram.h"
51
52 #if USE_JIT
53 extern void flush_icache_range(uint32 start, uint32 size); // from compemu_support.cpp
54 #endif
55
56 #ifdef ENABLE_MON
57 # include "mon.h"
58 #endif
59
60 #define DEBUG 0
61 #include "debug.h"
62
63
64 #import <AppKit/AppKit.h>
65
66 #include "main_macosx.h" // To bridge between main() and misc. classes
67
68
69 // Constants
70 const char ROM_FILE_NAME[] = "ROM";
71 const int SCRATCH_MEM_SIZE = 0x10000; // Size of scratch memory area
72
73
74 // CPU and FPU type, addressing mode
75 int CPUType;
76 bool CPUIs68060;
77 int FPUType;
78 bool TwentyFourBitAddressing;
79
80
81 // Global variables
82
83 #ifdef HAVE_PTHREADS
84
85 static pthread_mutex_t intflag_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect InterruptFlags
86 #define LOCK_INTFLAGS pthread_mutex_lock(&intflag_lock)
87 #define UNLOCK_INTFLAGS pthread_mutex_unlock(&intflag_lock)
88
89 #else
90
91 #define LOCK_INTFLAGS
92 #define UNLOCK_INTFLAGS
93
94 #endif
95
96 #if USE_SCRATCHMEM_SUBTERFUGE
97 uint8 *ScratchMem = NULL; // Scratch memory for Mac ROM writes
98 #endif
99
100 #ifdef ENABLE_MON
101 static struct sigaction sigint_sa; // sigaction for SIGINT handler
102 static void sigint_handler(...);
103 #endif
104
105 #if REAL_ADDRESSING
106 static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
107 #endif
108
109
110 /*
111 * SIGSEGV handler
112 */
113
114 static sigsegv_return_t sigsegv_handler(sigsegv_address_t fault_address,
115 sigsegv_address_t fault_instruction)
116 {
117 #if ENABLE_VOSF
118 // Handle screen fault
119 extern bool Screen_fault_handler(sigsegv_address_t, sigsegv_address_t);
120 if (Screen_fault_handler(fault_address, fault_instruction))
121 return SIGSEGV_RETURN_SUCCESS;
122 #endif
123
124 #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
125 // Ignore writes to ROM
126 if (((uintptr)fault_address - (uintptr)ROMBaseHost) < ROMSize)
127 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
128
129 // Ignore all other faults, if requested
130 if (PrefsFindBool("ignoresegv"))
131 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
132 #endif
133
134 return SIGSEGV_RETURN_FAILURE;
135 }
136
137
138 /*
139 * Dump state when everything went wrong after a SEGV
140 */
141
142 static void sigsegv_dump_state(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
143 {
144 fprintf(stderr, "Caught SIGSEGV at address %p", fault_address);
145 if (fault_instruction != SIGSEGV_INVALID_PC)
146 fprintf(stderr, " [IP=%p]", fault_instruction);
147 fprintf(stderr, "\n");
148 uaecptr nextpc;
149 extern void m68k_dumpstate(uaecptr *nextpc);
150 m68k_dumpstate(&nextpc);
151 #if USE_JIT && JIT_DEBUG
152 extern void compiler_dumpstate(void);
153 compiler_dumpstate();
154 #endif
155 VideoQuitFullScreen();
156 #ifdef ENABLE_MON
157 char *arg[4] = {"mon", "-m", "-r", NULL};
158 mon(3, arg);
159 QuitEmulator();
160 #endif
161 }
162
163
164 /*
165 * Main program
166 */
167
168 static void usage(const char *prg_name)
169 {
170 printf("Usage: %s [OPTION...]\n", prg_name);
171 printf("\nUnix options:\n");
172 printf(" --help\n display this usage message\n");
173 printf(" --config FILE\n read/write configuration from/to FILE\n");
174 printf(" --break ADDRESS\n set ROM breakpoint\n");
175 printf(" --rominfo\n dump ROM information\n");
176 LoadPrefs(); // read the prefs file so PrefsPrintUsage() will print the correct default values
177 PrefsPrintUsage();
178 exit(0);
179 }
180
181 int main(int argc, char **argv)
182 {
183 // Initialize variables
184 RAMBaseHost = NULL;
185 ROMBaseHost = NULL;
186 srand(time(NULL));
187 tzset();
188
189 // Print some info
190 printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
191 printf(" %s\n", GetString(STR_ABOUT_TEXT2));
192
193 // Parse command line arguments
194 for (int i=1; i<argc; i++) {
195 if (strcmp(argv[i], "--help") == 0) {
196 usage(argv[0]);
197 } else if (strncmp(argv[i], "-psn_", 5) == 0) {// OS X process identifier
198 i++;
199 } else if (strcmp(argv[i], "--break") == 0) {
200 i++;
201 if (i < argc)
202 ROMBreakpoint = strtol(argv[i], NULL, 0);
203 } else if (strcmp(argv[i], "--config") == 0) {
204 argv[i++] = NULL;
205 if (i < argc) {
206 extern string UserPrefsPath; // from prefs_unix.cpp
207 UserPrefsPath = argv[i];
208 argv[i] = NULL;
209 }
210 } else if (strcmp(argv[i], "--rominfo") == 0) {
211 PrintROMInfo = true;
212 } else if (argv[i][0] == '-') {
213 fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
214 usage(argv[0]);
215 }
216 }
217
218 // Read preferences
219 PrefsInit(argc, argv);
220
221 // Init system routines
222 SysInit();
223
224 // Open display, attach to window server,
225 // load pre-instantiated classes from MainMenu.nib, start run loop
226 int i = NSApplicationMain(argc, (const char **)argv);
227 // We currently never get past here, because QuitEmulator() does an exit()
228
229 // Exit system routines
230 SysExit();
231
232 // Exit preferences
233 PrefsExit();
234
235 return i;
236 }
237
238 #define QuitEmulator() { QuitEmuNoExit() ; return NO; }
239
240 bool InitEmulator (void)
241 {
242 char str[256];
243
244
245 // Install the handler for SIGSEGV
246 if (!sigsegv_install_handler(sigsegv_handler)) {
247 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGSEGV", strerror(errno));
248 ErrorAlert(str);
249 QuitEmulator();
250 }
251
252 // Register dump state function when we got mad after a segfault
253 sigsegv_set_dump_state(sigsegv_dump_state);
254
255 // Read RAM size
256 RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary
257 if (RAMSize < 1024*1024) {
258 WarningAlert(GetString(STR_SMALL_RAM_WARN));
259 RAMSize = 1024*1024;
260 }
261
262 #if REAL_ADDRESSING || DIRECT_ADDRESSING
263 RAMSize = RAMSize & -getpagesize(); // Round down to page boundary
264 #endif
265
266 // Initialize VM system
267 vm_init();
268
269 #if REAL_ADDRESSING
270 // Flag: RAM and ROM are contigously allocated from address 0
271 bool memory_mapped_from_zero = false;
272
273 // Under Solaris/SPARC and NetBSD/m68k, Basilisk II is known to crash
274 // when trying to map a too big chunk of memory starting at address 0
275 #if defined(OS_solaris) || defined(OS_netbsd) || defined(PAGEZERO_HACK)
276 const bool can_map_all_memory = false;
277 #else
278 const bool can_map_all_memory = true;
279 #endif
280
281 // Try to allocate all memory from 0x0000, if it is not known to crash
282 if (can_map_all_memory && (vm_acquire_fixed(0, RAMSize + 0x100000) == 0)) {
283 D(bug("Could allocate RAM and ROM from 0x0000\n"));
284 memory_mapped_from_zero = true;
285 }
286
287 #ifndef PAGEZERO_HACK
288 // Otherwise, just create the Low Memory area (0x0000..0x2000)
289 else if (vm_acquire_fixed(0, 0x2000) == 0) {
290 D(bug("Could allocate the Low Memory globals\n"));
291 lm_area_mapped = true;
292 }
293
294 // Exit on failure
295 else {
296 sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno));
297 ErrorAlert(str);
298 QuitEmulator();
299 }
300 #endif
301 #else
302 *str = 0; // Eliminate unused variable warning
303 #endif /* REAL_ADDRESSING */
304
305 // Create areas for Mac RAM and ROM
306 #if REAL_ADDRESSING
307 if (memory_mapped_from_zero) {
308 RAMBaseHost = (uint8 *)0;
309 ROMBaseHost = RAMBaseHost + RAMSize;
310 }
311 else
312 #endif
313 {
314 RAMBaseHost = (uint8 *)vm_acquire(RAMSize);
315 ROMBaseHost = (uint8 *)vm_acquire(0x100000);
316 if (RAMBaseHost == VM_MAP_FAILED || ROMBaseHost == VM_MAP_FAILED) {
317 ErrorAlert(STR_NO_MEM_ERR);
318 QuitEmulator();
319 }
320 }
321
322 #if USE_SCRATCHMEM_SUBTERFUGE
323 // Allocate scratch memory
324 ScratchMem = (uint8 *)vm_acquire(SCRATCH_MEM_SIZE);
325 if (ScratchMem == VM_MAP_FAILED) {
326 ErrorAlert(STR_NO_MEM_ERR);
327 QuitEmulator();
328 }
329 ScratchMem += SCRATCH_MEM_SIZE/2; // ScratchMem points to middle of block
330 #endif
331
332 #if DIRECT_ADDRESSING
333 // RAMBaseMac shall always be zero
334 MEMBaseDiff = (uintptr)RAMBaseHost;
335 RAMBaseMac = 0;
336 ROMBaseMac = Host2MacAddr(ROMBaseHost);
337 #endif
338 #if REAL_ADDRESSING
339 RAMBaseMac = (uint32)RAMBaseHost;
340 ROMBaseMac = (uint32)ROMBaseHost;
341 #endif
342 D(bug("Mac RAM starts at %p (%08x)\n", RAMBaseHost, RAMBaseMac));
343 D(bug("Mac ROM starts at %p (%08x)\n", ROMBaseHost, ROMBaseMac));
344
345 // Get rom file path from preferences
346 const char *rom_path = PrefsFindString("rom");
347 if ( ! rom_path )
348 WarningAlert("No rom pathname set. Trying ./ROM");
349
350 // Load Mac ROM
351 int rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY);
352 if (rom_fd < 0) {
353 ErrorAlert(STR_NO_ROM_FILE_ERR);
354 QuitEmulator();
355 }
356 printf(GetString(STR_READING_ROM_FILE));
357 ROMSize = lseek(rom_fd, 0, SEEK_END);
358 if (ROMSize != 64*1024 && ROMSize != 128*1024 && ROMSize != 256*1024 && ROMSize != 512*1024 && ROMSize != 1024*1024) {
359 ErrorAlert(STR_ROM_SIZE_ERR);
360 close(rom_fd);
361 QuitEmulator();
362 }
363 lseek(rom_fd, 0, SEEK_SET);
364 if (read(rom_fd, ROMBaseHost, ROMSize) != (ssize_t)ROMSize) {
365 ErrorAlert(STR_ROM_FILE_READ_ERR);
366 close(rom_fd);
367 QuitEmulator();
368 }
369
370
371 // Initialize everything
372 if (!InitAll())
373 QuitEmulator();
374 D(bug("Initialization complete\n"));
375
376
377 #ifdef ENABLE_MON
378 // Setup SIGINT handler to enter mon
379 sigemptyset(&sigint_sa.sa_mask);
380 sigint_sa.sa_handler = (void (*)(int))sigint_handler;
381 sigint_sa.sa_flags = 0;
382 sigaction(SIGINT, &sigint_sa, NULL);
383 #endif
384
385
386 return YES;
387 }
388
389 #undef QuitEmulator()
390
391
392 /*
393 * Quit emulator
394 */
395
396 void QuitEmuNoExit()
397 {
398 D(bug("QuitEmulator\n"));
399
400 // Exit 680x0 emulation
401 Exit680x0();
402
403 // Deinitialize everything
404 ExitAll();
405
406 // Free ROM/RAM areas
407 if (RAMBaseHost != VM_MAP_FAILED) {
408 vm_release(RAMBaseHost, RAMSize);
409 RAMBaseHost = NULL;
410 }
411 if (ROMBaseHost != VM_MAP_FAILED) {
412 vm_release(ROMBaseHost, 0x100000);
413 ROMBaseHost = NULL;
414 }
415
416 #if USE_SCRATCHMEM_SUBTERFUGE
417 // Delete scratch memory area
418 if (ScratchMem != (uint8 *)VM_MAP_FAILED) {
419 vm_release((void *)(ScratchMem - SCRATCH_MEM_SIZE/2), SCRATCH_MEM_SIZE);
420 ScratchMem = NULL;
421 }
422 #endif
423
424 #if REAL_ADDRESSING
425 // Delete Low Memory area
426 if (lm_area_mapped)
427 vm_release(0, 0x2000);
428 #endif
429
430 // Exit VM wrappers
431 vm_exit();
432
433 // Exit system routines
434 SysExit();
435
436 // Exit preferences
437 PrefsExit();
438 }
439
440 void QuitEmulator(void)
441 {
442 extern NSApplication *NSApp;
443
444
445 QuitEmuNoExit();
446
447 // Stop run loop?
448 [NSApp terminate: nil];
449
450 exit(0);
451 }
452
453
454 /*
455 * Code was patched, flush caches if neccessary (i.e. when using a real 680x0
456 * or a dynamically recompiling emulator)
457 */
458
459 void FlushCodeCache(void *start, uint32 size)
460 {
461 #if USE_JIT
462 if (UseJIT)
463 flush_icache_range((uintptr)start, size);
464 #endif
465 }
466
467
468 /*
469 * SIGINT handler, enters mon
470 */
471
472 #ifdef ENABLE_MON
473 static void sigint_handler(...)
474 {
475 uaecptr nextpc;
476 extern void m68k_dumpstate(uaecptr *nextpc);
477 m68k_dumpstate(&nextpc);
478 VideoQuitFullScreen();
479 char *arg[4] = {"mon", "-m", "-r", NULL};
480 mon(3, arg);
481 QuitEmulator();
482 }
483 #endif
484
485
486 /*
487 * Mutexes
488 */
489
490 #ifdef HAVE_PTHREADS
491
492 struct B2_mutex {
493 B2_mutex() {
494 pthread_mutexattr_t attr;
495 pthread_mutexattr_init(&attr);
496 // Initialize the mutex for priority inheritance --
497 // required for accurate timing.
498 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
499 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
500 #endif
501 #if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL)
502 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
503 #endif
504 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
505 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
506 #endif
507 pthread_mutex_init(&m, &attr);
508 pthread_mutexattr_destroy(&attr);
509 }
510 ~B2_mutex() {
511 pthread_mutex_trylock(&m); // Make sure it's locked before
512 pthread_mutex_unlock(&m); // unlocking it.
513 pthread_mutex_destroy(&m);
514 }
515 pthread_mutex_t m;
516 };
517
518 B2_mutex *B2_create_mutex(void)
519 {
520 return new B2_mutex;
521 }
522
523 void B2_lock_mutex(B2_mutex *mutex)
524 {
525 pthread_mutex_lock(&mutex->m);
526 }
527
528 void B2_unlock_mutex(B2_mutex *mutex)
529 {
530 pthread_mutex_unlock(&mutex->m);
531 }
532
533 void B2_delete_mutex(B2_mutex *mutex)
534 {
535 delete mutex;
536 }
537
538 #else
539
540 struct B2_mutex {
541 int dummy;
542 };
543
544 B2_mutex *B2_create_mutex(void)
545 {
546 return new B2_mutex;
547 }
548
549 void B2_lock_mutex(B2_mutex *mutex)
550 {
551 }
552
553 void B2_unlock_mutex(B2_mutex *mutex)
554 {
555 }
556
557 void B2_delete_mutex(B2_mutex *mutex)
558 {
559 delete mutex;
560 }
561
562 #endif
563
564
565 /*
566 * Interrupt flags (must be handled atomically!)
567 */
568
569 uint32 InterruptFlags = 0;
570
571 void SetInterruptFlag(uint32 flag)
572 {
573 LOCK_INTFLAGS;
574 InterruptFlags |= flag;
575 UNLOCK_INTFLAGS;
576 }
577
578 void ClearInterruptFlag(uint32 flag)
579 {
580 LOCK_INTFLAGS;
581 InterruptFlags &= ~flag;
582 UNLOCK_INTFLAGS;
583 }
584
585
586 /*
587 * Display error alert
588 */
589
590 void ErrorAlert(const char *text)
591 {
592 NSString *title = [NSString stringWithCString:
593 GetString(STR_ERROR_ALERT_TITLE) ];
594 NSString *error = [NSString stringWithCString: text];
595 NSString *button = [NSString stringWithCString: GetString(STR_QUIT_BUTTON) ];
596
597 NSLog(error);
598 if ( PrefsFindBool("nogui") )
599 return;
600 VideoQuitFullScreen();
601 NSRunCriticalAlertPanel(title, error, button, nil, nil);
602 }
603
604
605 /*
606 * Display warning alert
607 */
608
609 void WarningAlert(const char *text)
610 {
611 NSString *title = [NSString stringWithCString:
612 GetString(STR_WARNING_ALERT_TITLE) ];
613 NSString *warning = [NSString stringWithCString: text];
614 NSString *button = [NSString stringWithCString: GetString(STR_OK_BUTTON) ];
615
616 NSLog(warning);
617 if ( PrefsFindBool("nogui") )
618 return;
619 VideoQuitFullScreen();
620 NSRunAlertPanel(title, warning, button, nil, nil);
621 }
622
623
624 /*
625 * Display choice alert
626 */
627
628 bool ChoiceAlert(const char *text, const char *pos, const char *neg)
629 {
630 NSString *title = [NSString stringWithCString:
631 GetString(STR_WARNING_ALERT_TITLE) ];
632 NSString *warning = [NSString stringWithCString: text];
633 NSString *yes = [NSString stringWithCString: pos];
634 NSString *no = [NSString stringWithCString: neg];
635
636 return NSRunInformationalAlertPanel(title, warning, yes, no, nil);
637 }