ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/main_macosx.mm
Revision: 1.17
Committed: 2007-01-13T18:21:30Z (17 years, 4 months ago) by gbeauche
Branch: MAIN
Changes since 1.16: +4 -26 lines
Log Message:
Remove the 33-bit addressing hack as it's overly complex for not much gain.
Rather, use an address override prefix (0x67) though Intel Core optimization
reference guide says to avoid LCP prefixes. In practise, impact on performance
is measurably marginal on e.g. Speedometer tests.

File Contents

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