ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/main_macosx.mm
Revision: 1.18
Committed: 2007-12-30T08:47:34Z (16 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.17: +9 -7 lines
Log Message:
Sync with the new SIGSEGV API.

File Contents

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