ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/main_macosx.mm
Revision: 1.22
Committed: 2009-10-08T08:56:23Z (14 years, 8 months ago) by nigel
Branch: MAIN
Changes since 1.21: +48 -23 lines
Log Message:
Re-synch with latest main_unix.cpp to work around method changes in LoadPrefs()
and PrefsInit(). Fixes build error reported by Jean-Jacques Cortes.

File Contents

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