ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/main_macosx.mm
Revision: 1.10
Committed: 2003-11-18T11:19:35Z (20 years, 6 months ago) by nigel
Branch: MAIN
Changes since 1.9: +72 -18 lines
Log Message:
Latest changes from Unix version (which I don't think work)

File Contents

# User Rev Content
1 nigel 1.1 /*
2 nigel 1.10 * $Id: main_macosx.mm,v 1.9 2003/08/02 11:03:15 nigel Exp $
3 nigel 1.1 *
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-2002 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 nigel 1.10 #define PTHREADS // Why is this here?
25 nigel 1.1 #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 nigel 1.8 #include <string>
36     using std::string;
37    
38 nigel 1.1 #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 nigel 1.8 #include "sigsegv.h"
45 nigel 1.1 #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 nigel 1.8 #if USE_JIT
53     extern void (*flush_icache)(int); // from compemu_support.cpp
54     #endif
55    
56 nigel 1.1 #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 nigel 1.3 #ifdef ENABLE_MON
101 nigel 1.1 static struct sigaction sigint_sa; // sigaction for SIGINT handler
102     static void sigint_handler(...);
103 nigel 1.3 #endif
104 nigel 1.1
105     #if REAL_ADDRESSING
106     static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
107     #endif
108    
109    
110 nigel 1.8 /*
111 nigel 1.10 * 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 nigel 1.8 * 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 nigel 1.1
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 nigel 1.8 printf(" --config FILE\n read/write configuration from/to FILE\n");
174 nigel 1.1 printf(" --break ADDRESS\n set ROM breakpoint\n");
175     printf(" --rominfo\n dump ROM information\n");
176 nigel 1.8 LoadPrefs(); // read the prefs file so PrefsPrintUsage() will print the correct default values
177 nigel 1.1 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 nigel 1.8 } 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 nigel 1.1 } 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 nigel 1.10 // Read preferences
219     PrefsInit(argc, argv);
220    
221 nigel 1.1 // 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 nigel 1.7 #define QuitEmulator() { QuitEmuNoExit() ; return NO; }
239 nigel 1.1
240     bool InitEmulator (void)
241     {
242     char str[256];
243    
244    
245 nigel 1.10 // 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 nigel 1.8
252     // Register dump state function when we got mad after a segfault
253     sigsegv_set_dump_state(sigsegv_dump_state);
254    
255 nigel 1.1 // 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 nigel 1.10 #if defined(OS_solaris) || defined(OS_netbsd) || defined(PAGEZERO_HACK)
276 nigel 1.1 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 nigel 1.10
287     #ifndef PAGEZERO_HACK
288 nigel 1.1 // 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 nigel 1.10 #endif
301 nigel 1.4 #else
302     *str = 0; // Eliminate unused variable warning
303 nigel 1.10 #endif /* REAL_ADDRESSING */
304 nigel 1.1
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 nigel 1.6 if ( ! rom_path )
348     WarningAlert("No rom pathname set. Trying ./ROM");
349 nigel 1.1
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 nigel 1.6 extern NSApplication *NSApp;
443    
444    
445 nigel 1.1 QuitEmuNoExit();
446 nigel 1.6
447     // Stop run loop?
448     [NSApp terminate: nil];
449    
450 nigel 1.1 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 nigel 1.8 #if USE_JIT
462     if (UseJIT)
463     flush_icache(-1);
464     #endif
465 nigel 1.1 }
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 nigel 1.10 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 nigel 1.1 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 nigel 1.10 if ( PrefsFindBool("nogui") )
599     return;
600     VideoQuitFullScreen();
601 nigel 1.1 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 nigel 1.10 if ( PrefsFindBool("nogui") )
618     return;
619     VideoQuitFullScreen();
620 nigel 1.1 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     }