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, 10 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

# User Rev Content
1 nigel 1.1 /*
2 gbeauche 1.17 * $Id: main_macosx.mm,v 1.16 2006/05/08 16:56:07 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     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 nigel 1.8 * 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 nigel 1.15 #endif
178 nigel 1.8 QuitEmulator();
179     }
180    
181 nigel 1.1
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 nigel 1.8 printf(" --config FILE\n read/write configuration from/to FILE\n");
192 nigel 1.1 printf(" --break ADDRESS\n set ROM breakpoint\n");
193     printf(" --rominfo\n dump ROM information\n");
194 nigel 1.8 LoadPrefs(); // read the prefs file so PrefsPrintUsage() will print the correct default values
195 nigel 1.1 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 nigel 1.8 } 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 nigel 1.1 } 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 nigel 1.10 // Read preferences
237     PrefsInit(argc, argv);
238    
239 nigel 1.1 // 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 nigel 1.7 #define QuitEmulator() { QuitEmuNoExit() ; return NO; }
257 nigel 1.1
258     bool InitEmulator (void)
259     {
260     char str[256];
261    
262    
263 nigel 1.10 // 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 nigel 1.8
270     // Register dump state function when we got mad after a segfault
271     sigsegv_set_dump_state(sigsegv_dump_state);
272    
273 nigel 1.1 // 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 nigel 1.15 if (RAMSize > 1023*1024*1024) // Cap to 1023MB (APD crashes at 1GB)
280     RAMSize = 1023*1024*1024;
281 nigel 1.1
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 nigel 1.15
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 nigel 1.1 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 nigel 1.15 if (can_map_all_memory && (vm_acquire_mac_fixed(0, RAMSize + 0x100000) == 0)) {
304 nigel 1.1 D(bug("Could allocate RAM and ROM from 0x0000\n"));
305     memory_mapped_from_zero = true;
306     }
307 nigel 1.15
308 nigel 1.10 #ifndef PAGEZERO_HACK
309 nigel 1.1 // Otherwise, just create the Low Memory area (0x0000..0x2000)
310 nigel 1.15 else if (vm_acquire_mac_fixed(0, 0x2000) == 0) {
311 nigel 1.1 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 nigel 1.10 #endif
322 nigel 1.4 #else
323     *str = 0; // Eliminate unused variable warning
324 nigel 1.10 #endif /* REAL_ADDRESSING */
325 nigel 1.1
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 nigel 1.14 uint8 *ram_rom_area = (uint8 *)vm_acquire_mac(RAMSize + 0x100000);
336     if (ram_rom_area == VM_MAP_FAILED) {
337 nigel 1.1 ErrorAlert(STR_NO_MEM_ERR);
338     QuitEmulator();
339     }
340 nigel 1.14 RAMBaseHost = ram_rom_area;
341     ROMBaseHost = RAMBaseHost + RAMSize;
342 nigel 1.1 }
343    
344     #if USE_SCRATCHMEM_SUBTERFUGE
345     // Allocate scratch memory
346 nigel 1.15 ScratchMem = (uint8 *)vm_acquire_mac(SCRATCH_MEM_SIZE);
347 nigel 1.1 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 nigel 1.15 RAMBaseMac = Host2MacAddr(RAMBaseHost);
362     ROMBaseMac = Host2MacAddr(ROMBaseHost);
363 nigel 1.1 #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 nigel 1.6 if ( ! rom_path )
370     WarningAlert("No rom pathname set. Trying ./ROM");
371 nigel 1.1
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 nigel 1.14 vm_release(RAMBaseHost, RAMSize + 0x100000);
431 nigel 1.1 RAMBaseHost = NULL;
432 nigel 1.15 ROMBaseHost = NULL;
433 nigel 1.1 }
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 nigel 1.6
463     // Stop run loop?
464     [NSApp terminate: nil];
465    
466 nigel 1.1 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 nigel 1.8 #if USE_JIT
478     if (UseJIT)
479 gbeauche 1.16 flush_icache_range((uint8 *)start, size);
480 nigel 1.8 #endif
481 nigel 1.1 }
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 nigel 1.14 #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 nigel 1.1 /*
536     * Mutexes
537     */
538    
539     #ifdef HAVE_PTHREADS
540    
541     struct B2_mutex {
542 nigel 1.10 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 nigel 1.1 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 nigel 1.10 if ( PrefsFindBool("nogui") )
648     return;
649     VideoQuitFullScreen();
650 nigel 1.1 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 nigel 1.10 if ( PrefsFindBool("nogui") )
667     return;
668     VideoQuitFullScreen();
669 nigel 1.1 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     }