ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/main_macosx.mm
Revision: 1.16
Committed: 2006-05-08T16:56:07Z (18 years, 1 month ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-19
Changes since 1.15: +3 -3 lines
Log Message:
Fix for LAZY_FLUSH_ICACHE_RANGE. Blocks are indexed by native addresses.

File Contents

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