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, 5 months 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

# User Rev Content
1 nigel 1.1 /*
2 gbeauche 1.16 * $Id: main_macosx.mm,v 1.15 2006/03/11 11:45:25 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 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 nigel 1.14 bool ThirtyThreeBitAddressing = false;
82 nigel 1.1
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 nigel 1.3 #ifdef ENABLE_MON
104 nigel 1.1 static struct sigaction sigint_sa; // sigaction for SIGINT handler
105     static void sigint_handler(...);
106 nigel 1.3 #endif
107 nigel 1.1
108     #if REAL_ADDRESSING
109     static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
110     #endif
111    
112    
113 nigel 1.8 /*
114 nigel 1.15 * Helpers to map memory that can be accessed from the Mac side
115 nigel 1.14 */
116    
117 nigel 1.15 // NOTE: VM_MAP_33BIT is only used when compiling a 64-bit JIT on specific platforms
118 nigel 1.14 void *vm_acquire_mac(size_t size)
119     {
120     void *m = vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_33BIT);
121 nigel 1.15 #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 nigel 1.14 ThirtyThreeBitAddressing = false;
125     m = vm_acquire(size);
126     }
127 nigel 1.15 #endif
128 nigel 1.14 return m;
129     }
130    
131 nigel 1.15 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 nigel 1.14
145     /*
146 nigel 1.10 * 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 nigel 1.8 * 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 nigel 1.15 #endif
195 nigel 1.8 QuitEmulator();
196     }
197    
198 nigel 1.1
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 nigel 1.8 printf(" --config FILE\n read/write configuration from/to FILE\n");
209 nigel 1.1 printf(" --break ADDRESS\n set ROM breakpoint\n");
210     printf(" --rominfo\n dump ROM information\n");
211 nigel 1.8 LoadPrefs(); // read the prefs file so PrefsPrintUsage() will print the correct default values
212 nigel 1.1 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 nigel 1.8 } 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 nigel 1.1 } 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 nigel 1.10 // Read preferences
254     PrefsInit(argc, argv);
255    
256 nigel 1.1 // 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 nigel 1.7 #define QuitEmulator() { QuitEmuNoExit() ; return NO; }
274 nigel 1.1
275     bool InitEmulator (void)
276     {
277     char str[256];
278    
279    
280 nigel 1.10 // 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 nigel 1.8
287     // Register dump state function when we got mad after a segfault
288     sigsegv_set_dump_state(sigsegv_dump_state);
289    
290 nigel 1.1 // 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 nigel 1.15 if (RAMSize > 1023*1024*1024) // Cap to 1023MB (APD crashes at 1GB)
297     RAMSize = 1023*1024*1024;
298 nigel 1.1
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 nigel 1.15 #ifdef USE_33BIT_ADDRESSING
307     // Speculatively enables 33-bit addressing
308     ThirtyThreeBitAddressing = true;
309     #endif
310    
311 nigel 1.1 #if REAL_ADDRESSING
312     // Flag: RAM and ROM are contigously allocated from address 0
313     bool memory_mapped_from_zero = false;
314 nigel 1.15
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 nigel 1.1 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 nigel 1.15 if (can_map_all_memory && (vm_acquire_mac_fixed(0, RAMSize + 0x100000) == 0)) {
326 nigel 1.1 D(bug("Could allocate RAM and ROM from 0x0000\n"));
327     memory_mapped_from_zero = true;
328     }
329 nigel 1.15
330 nigel 1.10 #ifndef PAGEZERO_HACK
331 nigel 1.1 // Otherwise, just create the Low Memory area (0x0000..0x2000)
332 nigel 1.15 else if (vm_acquire_mac_fixed(0, 0x2000) == 0) {
333 nigel 1.1 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 nigel 1.10 #endif
344 nigel 1.4 #else
345     *str = 0; // Eliminate unused variable warning
346 nigel 1.10 #endif /* REAL_ADDRESSING */
347 nigel 1.1
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 nigel 1.14 uint8 *ram_rom_area = (uint8 *)vm_acquire_mac(RAMSize + 0x100000);
358     if (ram_rom_area == VM_MAP_FAILED) {
359 nigel 1.1 ErrorAlert(STR_NO_MEM_ERR);
360     QuitEmulator();
361     }
362 nigel 1.14 RAMBaseHost = ram_rom_area;
363     ROMBaseHost = RAMBaseHost + RAMSize;
364 nigel 1.1 }
365    
366     #if USE_SCRATCHMEM_SUBTERFUGE
367     // Allocate scratch memory
368 nigel 1.15 ScratchMem = (uint8 *)vm_acquire_mac(SCRATCH_MEM_SIZE);
369 nigel 1.1 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 nigel 1.15 RAMBaseMac = Host2MacAddr(RAMBaseHost);
384     ROMBaseMac = Host2MacAddr(ROMBaseHost);
385 nigel 1.1 #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 nigel 1.6 if ( ! rom_path )
392     WarningAlert("No rom pathname set. Trying ./ROM");
393 nigel 1.1
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 nigel 1.14 vm_release(RAMBaseHost, RAMSize + 0x100000);
453 nigel 1.1 RAMBaseHost = NULL;
454 nigel 1.15 ROMBaseHost = NULL;
455 nigel 1.1 }
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 nigel 1.6
485     // Stop run loop?
486     [NSApp terminate: nil];
487    
488 nigel 1.1 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 nigel 1.8 #if USE_JIT
500     if (UseJIT)
501 gbeauche 1.16 flush_icache_range((uint8 *)start, size);
502 nigel 1.8 #endif
503 nigel 1.1 }
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 nigel 1.14 #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 nigel 1.1 /*
558     * Mutexes
559     */
560    
561     #ifdef HAVE_PTHREADS
562    
563     struct B2_mutex {
564 nigel 1.10 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 nigel 1.1 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 nigel 1.10 if ( PrefsFindBool("nogui") )
670     return;
671     VideoQuitFullScreen();
672 nigel 1.1 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 nigel 1.10 if ( PrefsFindBool("nogui") )
689     return;
690     VideoQuitFullScreen();
691 nigel 1.1 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     }