ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.53
Committed: 2003-07-25T13:41:58Z (21 years ago) by cebix
Branch: MAIN
Changes since 1.52: +2 -0 lines
Log Message:
fix for no-threads case for an emulated 68k [Todd Vierling]

File Contents

# Content
1 /*
2 * main_unix.cpp - Startup code for Unix
3 *
4 * Basilisk II (C) 1997-2002 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <signal.h>
26 #include <errno.h>
27 #include <X11/Xlib.h>
28
29 #ifdef HAVE_PTHREADS
30 # include <pthread.h>
31 #endif
32
33 #if REAL_ADDRESSING || DIRECT_ADDRESSING
34 # include <sys/mman.h>
35 #endif
36
37 #if !EMULATED_68K && defined(__NetBSD__)
38 # include <m68k/sync_icache.h>
39 # include <m68k/frame.h>
40 # include <sys/param.h>
41 # include <sys/sysctl.h>
42 struct sigstate {
43 int ss_flags;
44 struct frame ss_frame;
45 struct fpframe ss_fpstate;
46 };
47 # define SS_FPSTATE 0x02
48 # define SS_USERREGS 0x04
49 #endif
50
51 #ifdef ENABLE_GTK
52 # include <gtk/gtk.h>
53 # include <gdk/gdk.h>
54 # ifdef HAVE_GNOMEUI
55 # include <gnome.h>
56 # endif
57 #endif
58
59 #ifdef ENABLE_XF86_DGA
60 # include <X11/Xutil.h>
61 # include <X11/extensions/xf86dga.h>
62 #endif
63
64 #include <string>
65 using std::string;
66
67 #include "cpu_emulation.h"
68 #include "sys.h"
69 #include "rom_patches.h"
70 #include "xpram.h"
71 #include "timer.h"
72 #include "video.h"
73 #include "emul_op.h"
74 #include "prefs.h"
75 #include "prefs_editor.h"
76 #include "macos_util.h"
77 #include "user_strings.h"
78 #include "version.h"
79 #include "main.h"
80 #include "vm_alloc.h"
81 #include "sigsegv.h"
82
83 #if USE_JIT
84 extern void (*flush_icache)(int); // from compemu_support.cpp
85 #endif
86
87 #ifdef ENABLE_MON
88 # include "mon.h"
89 #endif
90
91 #define DEBUG 0
92 #include "debug.h"
93
94
95 // Constants
96 const char ROM_FILE_NAME[] = "ROM";
97 #if !EMULATED_68K
98 const int SIG_STACK_SIZE = SIGSTKSZ; // Size of signal stack
99 #endif
100 const int SCRATCH_MEM_SIZE = 0x10000; // Size of scratch memory area
101
102
103 #if !EMULATED_68K
104 // RAM and ROM pointers
105 uint32 RAMBaseMac; // RAM base (Mac address space)
106 uint8 *RAMBaseHost; // RAM base (host address space)
107 uint32 RAMSize; // Size of RAM
108 uint32 ROMBaseMac; // ROM base (Mac address space)
109 uint8 *ROMBaseHost; // ROM base (host address space)
110 uint32 ROMSize; // Size of ROM
111 #endif
112
113
114 // CPU and FPU type, addressing mode
115 int CPUType;
116 bool CPUIs68060;
117 int FPUType;
118 bool TwentyFourBitAddressing;
119
120
121 // Global variables
122 char *x_display_name = NULL; // X11 display name
123 Display *x_display = NULL; // X11 display handle
124
125 static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes
126
127 #ifdef HAVE_PTHREADS
128 #if !EMULATED_68K
129 static pthread_t emul_thread; // Handle of MacOS emulation thread (main thread)
130 #endif
131
132 static bool xpram_thread_active = false; // Flag: XPRAM watchdog installed
133 static volatile bool xpram_thread_cancel = false; // Flag: Cancel XPRAM thread
134 static pthread_t xpram_thread; // XPRAM watchdog
135
136 static bool tick_thread_active = false; // Flag: 60Hz thread installed
137 static volatile bool tick_thread_cancel = false; // Flag: Cancel 60Hz thread
138 static pthread_t tick_thread; // 60Hz thread
139 static pthread_attr_t tick_thread_attr; // 60Hz thread attributes
140
141 static pthread_mutex_t intflag_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect InterruptFlags
142 #define LOCK_INTFLAGS pthread_mutex_lock(&intflag_lock)
143 #define UNLOCK_INTFLAGS pthread_mutex_unlock(&intflag_lock)
144
145 #else
146
147 #define LOCK_INTFLAGS
148 #define UNLOCK_INTFLAGS
149
150 #endif
151
152 #if !EMULATED_68K
153 #define SIG_IRQ SIGUSR1
154 static struct sigaction sigirq_sa; // Virtual 68k interrupt signal
155 static struct sigaction sigill_sa; // Illegal instruction
156 static void *sig_stack = NULL; // Stack for signal handlers
157 uint16 EmulatedSR; // Emulated bits of SR (supervisor bit and interrupt mask)
158 #endif
159
160 #if USE_SCRATCHMEM_SUBTERFUGE
161 uint8 *ScratchMem = NULL; // Scratch memory for Mac ROM writes
162 #endif
163
164 #if !defined(HAVE_PTHREADS)
165 static struct sigaction timer_sa; // sigaction used for timer
166
167 #if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
168 #define SIG_TIMER SIGRTMIN
169 static timer_t timer; // 60Hz timer
170 #endif
171 #endif // !HAVE_PTHREADS
172
173 #ifdef ENABLE_MON
174 static struct sigaction sigint_sa; // sigaction for SIGINT handler
175 static void sigint_handler(...);
176 #endif
177
178 #if REAL_ADDRESSING
179 static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
180 #endif
181
182
183 // Prototypes
184 static void *xpram_func(void *arg);
185 static void *tick_func(void *arg);
186 static void one_tick(...);
187 #if !EMULATED_68K
188 static void sigirq_handler(int sig, int code, struct sigcontext *scp);
189 static void sigill_handler(int sig, int code, struct sigcontext *scp);
190 extern "C" void EmulOpTrampoline(void);
191 #endif
192
193
194 /*
195 * Ersatz functions
196 */
197
198 extern "C" {
199
200 #ifndef HAVE_STRDUP
201 char *strdup(const char *s)
202 {
203 char *n = (char *)malloc(strlen(s) + 1);
204 strcpy(n, s);
205 return n;
206 }
207 #endif
208
209 }
210
211
212 /*
213 * Dump state when everything went wrong after a SEGV
214 */
215
216 static void sigsegv_dump_state(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
217 {
218 fprintf(stderr, "Caught SIGSEGV at address %p", fault_address);
219 if (fault_instruction != SIGSEGV_INVALID_PC)
220 fprintf(stderr, " [IP=%p]", fault_instruction);
221 fprintf(stderr, "\n");
222 #if EMULATED_68K
223 uaecptr nextpc;
224 extern void m68k_dumpstate(uaecptr *nextpc);
225 m68k_dumpstate(&nextpc);
226 #endif
227 #if USE_JIT && JIT_DEBUG
228 extern void compiler_dumpstate(void);
229 compiler_dumpstate();
230 #endif
231 VideoQuitFullScreen();
232 #ifdef ENABLE_MON
233 char *arg[4] = {"mon", "-m", "-r", NULL};
234 mon(3, arg);
235 QuitEmulator();
236 #endif
237 }
238
239
240 /*
241 * Main program
242 */
243
244 static void usage(const char *prg_name)
245 {
246 printf(
247 "Usage: %s [OPTION...]\n"
248 "\nUnix options:\n"
249 " --config FILE\n read/write configuration from/to FILE\n"
250 " --display STRING\n X display to use\n"
251 " --break ADDRESS\n set ROM breakpoint\n"
252 " --rominfo\n dump ROM information\n", prg_name
253 );
254 LoadPrefs(); // read the prefs file so PrefsPrintUsage() will print the correct default values
255 PrefsPrintUsage();
256 exit(0);
257 }
258
259 int main(int argc, char **argv)
260 {
261 char str[256];
262
263 // Initialize variables
264 RAMBaseHost = NULL;
265 ROMBaseHost = NULL;
266 srand(time(NULL));
267 tzset();
268
269 // Print some info
270 printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
271 printf(" %s\n", GetString(STR_ABOUT_TEXT2));
272
273 // Parse command line arguments
274 for (int i=1; i<argc; i++) {
275 if (strcmp(argv[i], "--help") == 0) {
276 usage(argv[0]);
277 } else if (strcmp(argv[i], "--display") == 0) {
278 i++; // don't remove the argument, gtk_init() needs it too
279 if (i < argc)
280 x_display_name = strdup(argv[i]);
281 } else if (strcmp(argv[i], "--break") == 0) {
282 argv[i++] = NULL;
283 if (i < argc) {
284 ROMBreakpoint = strtol(argv[i], NULL, 0);
285 argv[i] = NULL;
286 }
287 } else if (strcmp(argv[i], "--config") == 0) {
288 argv[i++] = NULL;
289 if (i < argc) {
290 extern string UserPrefsPath; // from prefs_unix.cpp
291 UserPrefsPath = argv[i];
292 argv[i] = NULL;
293 }
294 } else if (strcmp(argv[i], "--rominfo") == 0) {
295 argv[i] = NULL;
296 PrintROMInfo = true;
297 }
298 }
299
300 // Remove processed arguments
301 for (int i=1; i<argc; i++) {
302 int k;
303 for (k=i; k<argc; k++)
304 if (argv[k] != NULL)
305 break;
306 if (k > i) {
307 k -= i;
308 for (int j=i+k; j<argc; j++)
309 argv[j-k] = argv[j];
310 argc -= k;
311 }
312 }
313
314 #ifdef ENABLE_GTK
315 #ifdef HAVE_GNOMEUI
316 // Init GNOME/GTK
317 char version[16];
318 sprintf(version, "%d.%d", VERSION_MAJOR, VERSION_MINOR);
319 gnome_init("Basilisk II", version, argc, argv);
320 #else
321 // Init GTK
322 gtk_set_locale();
323 gtk_init(&argc, &argv);
324 #endif
325 #endif
326
327 // Read preferences
328 PrefsInit(argc, argv);
329
330 // Any command line arguments left?
331 for (int i=1; i<argc; i++) {
332 if (argv[i][0] == '-') {
333 fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
334 usage(argv[0]);
335 }
336 }
337
338 // Open display
339 x_display = XOpenDisplay(x_display_name);
340 if (x_display == NULL) {
341 char str[256];
342 sprintf(str, GetString(STR_NO_XSERVER_ERR), XDisplayName(x_display_name));
343 ErrorAlert(str);
344 QuitEmulator();
345 }
346
347 #if defined(ENABLE_XF86_DGA) && !defined(ENABLE_MON)
348 // Fork out, so we can return from fullscreen mode when things get ugly
349 XF86DGAForkApp(DefaultScreen(x_display));
350 #endif
351
352 // Init system routines
353 SysInit();
354
355 // Show preferences editor
356 if (!PrefsFindBool("nogui"))
357 if (!PrefsEditor())
358 QuitEmulator();
359
360 // Register request to ignore all segmentation faults
361 #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
362 if (PrefsFindBool("ignoresegv"))
363 sigsegv_add_ignore_range(0, ~(0UL), SIGSEGV_TRANSFER_LOAD | SIGSEGV_TRANSFER_STORE);
364 #endif
365
366 // Register dump state function when we got mad after a segfault
367 sigsegv_set_dump_state(sigsegv_dump_state);
368
369 // Read RAM size
370 RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary
371 if (RAMSize < 1024*1024) {
372 WarningAlert(GetString(STR_SMALL_RAM_WARN));
373 RAMSize = 1024*1024;
374 }
375
376 #if REAL_ADDRESSING || DIRECT_ADDRESSING
377 RAMSize = RAMSize & -getpagesize(); // Round down to page boundary
378 #endif
379
380 // Initialize VM system
381 vm_init();
382
383 #if REAL_ADDRESSING
384 // Flag: RAM and ROM are contigously allocated from address 0
385 bool memory_mapped_from_zero = false;
386
387 // Under Solaris/SPARC and NetBSD/m68k, Basilisk II is known to crash
388 // when trying to map a too big chunk of memory starting at address 0
389 #if defined(OS_solaris) || defined(OS_netbsd)
390 const bool can_map_all_memory = false;
391 #else
392 const bool can_map_all_memory = true;
393 #endif
394
395 // Try to allocate all memory from 0x0000, if it is not known to crash
396 if (can_map_all_memory && (vm_acquire_fixed(0, RAMSize + 0x100000) == 0)) {
397 D(bug("Could allocate RAM and ROM from 0x0000\n"));
398 memory_mapped_from_zero = true;
399 }
400
401 // Otherwise, just create the Low Memory area (0x0000..0x2000)
402 else if (vm_acquire_fixed(0, 0x2000) == 0) {
403 D(bug("Could allocate the Low Memory globals\n"));
404 lm_area_mapped = true;
405 }
406
407 // Exit on failure
408 else {
409 sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno));
410 ErrorAlert(str);
411 QuitEmulator();
412 }
413 #endif
414
415 // Create areas for Mac RAM and ROM
416 #if REAL_ADDRESSING
417 if (memory_mapped_from_zero) {
418 RAMBaseHost = (uint8 *)0;
419 ROMBaseHost = RAMBaseHost + RAMSize;
420 }
421 else
422 #endif
423 {
424 RAMBaseHost = (uint8 *)vm_acquire(RAMSize);
425 ROMBaseHost = (uint8 *)vm_acquire(0x100000);
426 if (RAMBaseHost == VM_MAP_FAILED || ROMBaseHost == VM_MAP_FAILED) {
427 ErrorAlert(STR_NO_MEM_ERR);
428 QuitEmulator();
429 }
430 }
431
432 #if USE_SCRATCHMEM_SUBTERFUGE
433 // Allocate scratch memory
434 ScratchMem = (uint8 *)vm_acquire(SCRATCH_MEM_SIZE);
435 if (ScratchMem == VM_MAP_FAILED) {
436 ErrorAlert(STR_NO_MEM_ERR);
437 QuitEmulator();
438 }
439 ScratchMem += SCRATCH_MEM_SIZE/2; // ScratchMem points to middle of block
440 #endif
441
442 #if DIRECT_ADDRESSING
443 // RAMBaseMac shall always be zero
444 MEMBaseDiff = (uintptr)RAMBaseHost;
445 RAMBaseMac = 0;
446 ROMBaseMac = Host2MacAddr(ROMBaseHost);
447 #endif
448 #if REAL_ADDRESSING
449 RAMBaseMac = (uint32)RAMBaseHost;
450 ROMBaseMac = (uint32)ROMBaseHost;
451 #endif
452 D(bug("Mac RAM starts at %p (%08x)\n", RAMBaseHost, RAMBaseMac));
453 D(bug("Mac ROM starts at %p (%08x)\n", ROMBaseHost, ROMBaseMac));
454
455 // Get rom file path from preferences
456 const char *rom_path = PrefsFindString("rom");
457
458 // Load Mac ROM
459 int rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY);
460 if (rom_fd < 0) {
461 ErrorAlert(STR_NO_ROM_FILE_ERR);
462 QuitEmulator();
463 }
464 printf(GetString(STR_READING_ROM_FILE));
465 ROMSize = lseek(rom_fd, 0, SEEK_END);
466 if (ROMSize != 64*1024 && ROMSize != 128*1024 && ROMSize != 256*1024 && ROMSize != 512*1024 && ROMSize != 1024*1024) {
467 ErrorAlert(STR_ROM_SIZE_ERR);
468 close(rom_fd);
469 QuitEmulator();
470 }
471 lseek(rom_fd, 0, SEEK_SET);
472 if (read(rom_fd, ROMBaseHost, ROMSize) != (ssize_t)ROMSize) {
473 ErrorAlert(STR_ROM_FILE_READ_ERR);
474 close(rom_fd);
475 QuitEmulator();
476 }
477
478 #if !EMULATED_68K
479 // Get CPU model
480 int mib[2] = {CTL_HW, HW_MODEL};
481 char *model;
482 size_t model_len;
483 sysctl(mib, 2, NULL, &model_len, NULL, 0);
484 model = (char *)malloc(model_len);
485 sysctl(mib, 2, model, &model_len, NULL, 0);
486 D(bug("Model: %s\n", model));
487
488 // Set CPU and FPU type
489 CPUIs68060 = false;
490 if (strstr(model, "020"))
491 CPUType = 2;
492 else if (strstr(model, "030"))
493 CPUType = 3;
494 else if (strstr(model, "040"))
495 CPUType = 4;
496 else if (strstr(model, "060")) {
497 CPUType = 4;
498 CPUIs68060 = true;
499 } else {
500 printf("WARNING: Cannot detect CPU type, assuming 68020\n");
501 CPUType = 2;
502 }
503 FPUType = 1; // NetBSD has an FPU emulation, so the FPU ought to be available at all times
504 TwentyFourBitAddressing = false;
505 #endif
506
507 // Initialize everything
508 if (!InitAll())
509 QuitEmulator();
510 D(bug("Initialization complete\n"));
511
512 #if !EMULATED_68K
513 // (Virtual) supervisor mode, disable interrupts
514 EmulatedSR = 0x2700;
515
516 #ifdef HAVE_PTHREADS
517 // Get handle of main thread
518 emul_thread = pthread_self();
519 #endif
520
521 // Create and install stack for signal handlers
522 sig_stack = malloc(SIG_STACK_SIZE);
523 D(bug("Signal stack at %p\n", sig_stack));
524 if (sig_stack == NULL) {
525 ErrorAlert(STR_NOT_ENOUGH_MEMORY_ERR);
526 QuitEmulator();
527 }
528 stack_t new_stack;
529 new_stack.ss_sp = sig_stack;
530 new_stack.ss_flags = 0;
531 new_stack.ss_size = SIG_STACK_SIZE;
532 if (sigaltstack(&new_stack, NULL) < 0) {
533 sprintf(str, GetString(STR_SIGALTSTACK_ERR), strerror(errno));
534 ErrorAlert(str);
535 QuitEmulator();
536 }
537
538 // Install SIGILL handler for emulating privileged instructions and
539 // executing A-Trap and EMUL_OP opcodes
540 sigemptyset(&sigill_sa.sa_mask); // Block virtual 68k interrupts during SIGILL handling
541 sigaddset(&sigill_sa.sa_mask, SIG_IRQ);
542 sigaddset(&sigill_sa.sa_mask, SIGALRM);
543 sigill_sa.sa_handler = (void (*)(int))sigill_handler;
544 sigill_sa.sa_flags = SA_ONSTACK;
545 if (sigaction(SIGILL, &sigill_sa, NULL) < 0) {
546 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGILL", strerror(errno));
547 ErrorAlert(str);
548 QuitEmulator();
549 }
550
551 // Install virtual 68k interrupt signal handler
552 sigemptyset(&sigirq_sa.sa_mask);
553 sigaddset(&sigirq_sa.sa_mask, SIGALRM);
554 sigirq_sa.sa_handler = (void (*)(int))sigirq_handler;
555 sigirq_sa.sa_flags = SA_ONSTACK | SA_RESTART;
556 if (sigaction(SIG_IRQ, &sigirq_sa, NULL) < 0) {
557 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIG_IRQ", strerror(errno));
558 ErrorAlert(str);
559 QuitEmulator();
560 }
561 #endif
562
563 #ifdef ENABLE_MON
564 // Setup SIGINT handler to enter mon
565 sigemptyset(&sigint_sa.sa_mask);
566 sigint_sa.sa_handler = (void (*)(int))sigint_handler;
567 sigint_sa.sa_flags = 0;
568 sigaction(SIGINT, &sigint_sa, NULL);
569 #endif
570
571 #if defined(HAVE_PTHREADS)
572
573 // POSIX threads available, start 60Hz thread
574 Set_pthread_attr(&tick_thread_attr, 0);
575 tick_thread_active = (pthread_create(&tick_thread, &tick_thread_attr, tick_func, NULL) == 0);
576 if (!tick_thread_active) {
577 sprintf(str, GetString(STR_TICK_THREAD_ERR), strerror(errno));
578 ErrorAlert(str);
579 QuitEmulator();
580 }
581 D(bug("60Hz thread started\n"));
582
583 #elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
584
585 // POSIX.4 timers and real-time signals available, start 60Hz timer
586 sigemptyset(&timer_sa.sa_mask);
587 timer_sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))one_tick;
588 timer_sa.sa_flags = SA_SIGINFO | SA_RESTART;
589 if (sigaction(SIG_TIMER, &timer_sa, NULL) < 0) {
590 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIG_TIMER", strerror(errno));
591 ErrorAlert(str);
592 QuitEmulator();
593 }
594 struct sigevent timer_event;
595 timer_event.sigev_notify = SIGEV_SIGNAL;
596 timer_event.sigev_signo = SIG_TIMER;
597 if (timer_create(CLOCK_REALTIME, &timer_event, &timer) < 0) {
598 sprintf(str, GetString(STR_TIMER_CREATE_ERR), strerror(errno));
599 ErrorAlert(str);
600 QuitEmulator();
601 }
602 struct itimerspec req;
603 req.it_value.tv_sec = 0;
604 req.it_value.tv_nsec = 16625000;
605 req.it_interval.tv_sec = 0;
606 req.it_interval.tv_nsec = 16625000;
607 if (timer_settime(timer, 0, &req, NULL) < 0) {
608 sprintf(str, GetString(STR_TIMER_SETTIME_ERR), strerror(errno));
609 ErrorAlert(str);
610 QuitEmulator();
611 }
612 D(bug("60Hz timer started\n"));
613
614 #else
615
616 // Start 60Hz timer
617 sigemptyset(&timer_sa.sa_mask); // Block virtual 68k interrupts during SIGARLM handling
618 #if !EMULATED_68K
619 sigaddset(&timer_sa.sa_mask, SIG_IRQ);
620 #endif
621 timer_sa.sa_handler = one_tick;
622 timer_sa.sa_flags = SA_ONSTACK | SA_RESTART;
623 if (sigaction(SIGALRM, &timer_sa, NULL) < 0) {
624 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGALRM", strerror(errno));
625 ErrorAlert(str);
626 QuitEmulator();
627 }
628 struct itimerval req;
629 req.it_interval.tv_sec = req.it_value.tv_sec = 0;
630 req.it_interval.tv_usec = req.it_value.tv_usec = 16625;
631 setitimer(ITIMER_REAL, &req, NULL);
632
633 #endif
634
635 #ifdef HAVE_PTHREADS
636 // Start XPRAM watchdog thread
637 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
638 xpram_thread_active = (pthread_create(&xpram_thread, NULL, xpram_func, NULL) == 0);
639 D(bug("XPRAM thread started\n"));
640 #endif
641
642 // Start 68k and jump to ROM boot routine
643 D(bug("Starting emulation...\n"));
644 Start680x0();
645
646 QuitEmulator();
647 return 0;
648 }
649
650
651 /*
652 * Quit emulator
653 */
654
655 void QuitEmulator(void)
656 {
657 D(bug("QuitEmulator\n"));
658
659 #if EMULATED_68K
660 // Exit 680x0 emulation
661 Exit680x0();
662 #endif
663
664 #if defined(HAVE_PTHREADS)
665 // Stop 60Hz thread
666 if (tick_thread_active) {
667 tick_thread_cancel = true;
668 #ifdef HAVE_PTHREAD_CANCEL
669 pthread_cancel(tick_thread);
670 #endif
671 pthread_join(tick_thread, NULL);
672 }
673 #elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
674 // Stop 60Hz timer
675 timer_delete(timer);
676 #else
677 struct itimerval req;
678 req.it_interval.tv_sec = req.it_value.tv_sec = 0;
679 req.it_interval.tv_usec = req.it_value.tv_usec = 0;
680 setitimer(ITIMER_REAL, &req, NULL);
681 #endif
682
683 #ifdef HAVE_PTHREADS
684 // Stop XPRAM watchdog thread
685 if (xpram_thread_active) {
686 xpram_thread_cancel = true;
687 #ifdef HAVE_PTHREAD_CANCEL
688 pthread_cancel(xpram_thread);
689 #endif
690 pthread_join(xpram_thread, NULL);
691 }
692 #endif
693
694 // Deinitialize everything
695 ExitAll();
696
697 // Free ROM/RAM areas
698 if (RAMBaseHost != VM_MAP_FAILED) {
699 vm_release(RAMBaseHost, RAMSize);
700 RAMBaseHost = NULL;
701 }
702 if (ROMBaseHost != VM_MAP_FAILED) {
703 vm_release(ROMBaseHost, 0x100000);
704 ROMBaseHost = NULL;
705 }
706
707 #if USE_SCRATCHMEM_SUBTERFUGE
708 // Delete scratch memory area
709 if (ScratchMem != (uint8 *)VM_MAP_FAILED) {
710 vm_release((void *)(ScratchMem - SCRATCH_MEM_SIZE/2), SCRATCH_MEM_SIZE);
711 ScratchMem = NULL;
712 }
713 #endif
714
715 #if REAL_ADDRESSING
716 // Delete Low Memory area
717 if (lm_area_mapped)
718 vm_release(0, 0x2000);
719 #endif
720
721 // Exit VM wrappers
722 vm_exit();
723
724 // Exit system routines
725 SysExit();
726
727 // Exit preferences
728 PrefsExit();
729
730 // Close X11 server connection
731 if (x_display)
732 XCloseDisplay(x_display);
733
734 exit(0);
735 }
736
737
738 /*
739 * Code was patched, flush caches if neccessary (i.e. when using a real 680x0
740 * or a dynamically recompiling emulator)
741 */
742
743 void FlushCodeCache(void *start, uint32 size)
744 {
745 #if USE_JIT
746 if (UseJIT)
747 flush_icache(-1);
748 #endif
749 #if !EMULATED_68K && defined(__NetBSD__)
750 m68k_sync_icache(start, size);
751 #endif
752 }
753
754
755 /*
756 * SIGINT handler, enters mon
757 */
758
759 #ifdef ENABLE_MON
760 static void sigint_handler(...)
761 {
762 #if EMULATED_68K
763 uaecptr nextpc;
764 extern void m68k_dumpstate(uaecptr *nextpc);
765 m68k_dumpstate(&nextpc);
766 #endif
767 VideoQuitFullScreen();
768 char *arg[4] = {"mon", "-m", "-r", NULL};
769 mon(3, arg);
770 QuitEmulator();
771 }
772 #endif
773
774
775 #ifdef HAVE_PTHREADS
776 /*
777 * Pthread configuration
778 */
779
780 void Set_pthread_attr(pthread_attr_t *attr, int priority)
781 {
782 pthread_attr_init(attr);
783 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
784 // Some of these only work for superuser
785 if (geteuid() == 0) {
786 pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
787 pthread_attr_setschedpolicy(attr, SCHED_FIFO);
788 struct sched_param fifo_param;
789 fifo_param.sched_priority = ((sched_get_priority_min(SCHED_FIFO) +
790 sched_get_priority_max(SCHED_FIFO)) / 2 +
791 priority);
792 pthread_attr_setschedparam(attr, &fifo_param);
793 }
794 if (pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) != 0) {
795 #ifdef PTHREAD_SCOPE_BOUND_NP
796 // If system scope is not available (eg. we're not running
797 // with CAP_SCHED_MGT capability on an SGI box), try bound
798 // scope. It exposes pthread scheduling to the kernel,
799 // without setting realtime priority.
800 pthread_attr_setscope(attr, PTHREAD_SCOPE_BOUND_NP);
801 #endif
802 }
803 #endif
804 }
805 #endif // HAVE_PTHREADS
806
807
808 /*
809 * Mutexes
810 */
811
812 #ifdef HAVE_PTHREADS
813
814 struct B2_mutex {
815 B2_mutex() {
816 pthread_mutexattr_t attr;
817 pthread_mutexattr_init(&attr);
818 // Initialize the mutex for priority inheritance --
819 // required for accurate timing.
820 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
821 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
822 #endif
823 #if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL)
824 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
825 #endif
826 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
827 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
828 #endif
829 pthread_mutex_init(&m, &attr);
830 pthread_mutexattr_destroy(&attr);
831 }
832 ~B2_mutex() {
833 pthread_mutex_trylock(&m); // Make sure it's locked before
834 pthread_mutex_unlock(&m); // unlocking it.
835 pthread_mutex_destroy(&m);
836 }
837 pthread_mutex_t m;
838 };
839
840 B2_mutex *B2_create_mutex(void)
841 {
842 return new B2_mutex;
843 }
844
845 void B2_lock_mutex(B2_mutex *mutex)
846 {
847 pthread_mutex_lock(&mutex->m);
848 }
849
850 void B2_unlock_mutex(B2_mutex *mutex)
851 {
852 pthread_mutex_unlock(&mutex->m);
853 }
854
855 void B2_delete_mutex(B2_mutex *mutex)
856 {
857 delete mutex;
858 }
859
860 #else
861
862 struct B2_mutex {
863 int dummy;
864 };
865
866 B2_mutex *B2_create_mutex(void)
867 {
868 return new B2_mutex;
869 }
870
871 void B2_lock_mutex(B2_mutex *mutex)
872 {
873 }
874
875 void B2_unlock_mutex(B2_mutex *mutex)
876 {
877 }
878
879 void B2_delete_mutex(B2_mutex *mutex)
880 {
881 delete mutex;
882 }
883
884 #endif
885
886
887 /*
888 * Interrupt flags (must be handled atomically!)
889 */
890
891 uint32 InterruptFlags = 0;
892
893 #if EMULATED_68K
894 void SetInterruptFlag(uint32 flag)
895 {
896 LOCK_INTFLAGS;
897 InterruptFlags |= flag;
898 UNLOCK_INTFLAGS;
899 }
900
901 void ClearInterruptFlag(uint32 flag)
902 {
903 LOCK_INTFLAGS;
904 InterruptFlags &= ~flag;
905 UNLOCK_INTFLAGS;
906 }
907 #endif
908
909 #if !EMULATED_68K
910 void TriggerInterrupt(void)
911 {
912 #if defined(HAVE_PTHREADS)
913 pthread_kill(emul_thread, SIG_IRQ);
914 #else
915 raise(SIG_IRQ);
916 #endif
917 }
918
919 void TriggerNMI(void)
920 {
921 // not yet supported
922 }
923 #endif
924
925
926 /*
927 * XPRAM watchdog thread (saves XPRAM every minute)
928 */
929
930 static void xpram_watchdog(void)
931 {
932 if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
933 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
934 SaveXPRAM();
935 }
936 }
937
938 #ifdef HAVE_PTHREADS
939 static void *xpram_func(void *arg)
940 {
941 while (!xpram_thread_cancel) {
942 for (int i=0; i<60 && !xpram_thread_cancel; i++)
943 Delay_usec(999999); // Only wait 1 second so we quit promptly when xpram_thread_cancel becomes true
944 xpram_watchdog();
945 }
946 return NULL;
947 }
948 #endif
949
950
951 /*
952 * 60Hz thread (really 60.15Hz)
953 */
954
955 static void one_second(void)
956 {
957 // Pseudo Mac 1Hz interrupt, update local time
958 WriteMacInt32(0x20c, TimerDateTime());
959
960 SetInterruptFlag(INTFLAG_1HZ);
961 TriggerInterrupt();
962
963 #ifndef HAVE_PTHREADS
964 static int second_counter = 0;
965 if (++second_counter > 60) {
966 second_counter = 0;
967 xpram_watchdog();
968 }
969 #endif
970 }
971
972 static void one_tick(...)
973 {
974 static int tick_counter = 0;
975 if (++tick_counter > 60) {
976 tick_counter = 0;
977 one_second();
978 }
979
980 #ifndef HAVE_PTHREADS
981 // No threads available, perform video refresh and networking from here
982 VideoRefresh();
983 SetInterruptFlag(INTFLAG_ETHER);
984 #endif
985
986 // Trigger 60Hz interrupt
987 if (ROMVersion != ROM_VERSION_CLASSIC || HasMacStarted()) {
988 SetInterruptFlag(INTFLAG_60HZ);
989 TriggerInterrupt();
990 }
991 }
992
993 #ifdef HAVE_PTHREADS
994 static void *tick_func(void *arg)
995 {
996 uint64 start = GetTicks_usec();
997 int64 ticks = 0;
998 uint64 next = GetTicks_usec();
999 while (!tick_thread_cancel) {
1000 one_tick();
1001 next += 16625;
1002 int64 delay = next - GetTicks_usec();
1003 if (delay > 0)
1004 Delay_usec(delay);
1005 else if (delay < -16625)
1006 next = GetTicks_usec();
1007 ticks++;
1008 }
1009 uint64 end = GetTicks_usec();
1010 D(bug("%Ld ticks in %Ld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
1011 return NULL;
1012 }
1013 #endif
1014
1015
1016 #if !EMULATED_68K
1017 /*
1018 * Virtual 68k interrupt handler
1019 */
1020
1021 static void sigirq_handler(int sig, int code, struct sigcontext *scp)
1022 {
1023 // Interrupts disabled? Then do nothing
1024 if (EmulatedSR & 0x0700)
1025 return;
1026
1027 struct sigstate *state = (struct sigstate *)scp->sc_ap;
1028 M68kRegisters *regs = (M68kRegisters *)&state->ss_frame;
1029
1030 // Set up interrupt frame on stack
1031 uint32 a7 = regs->a[7];
1032 a7 -= 2;
1033 WriteMacInt16(a7, 0x64);
1034 a7 -= 4;
1035 WriteMacInt32(a7, scp->sc_pc);
1036 a7 -= 2;
1037 WriteMacInt16(a7, scp->sc_ps | EmulatedSR);
1038 scp->sc_sp = regs->a[7] = a7;
1039
1040 // Set interrupt level
1041 EmulatedSR |= 0x2100;
1042
1043 // Jump to MacOS interrupt handler on return
1044 scp->sc_pc = ReadMacInt32(0x64);
1045 }
1046
1047
1048 /*
1049 * SIGILL handler, for emulation of privileged instructions and executing
1050 * A-Trap and EMUL_OP opcodes
1051 */
1052
1053 static void sigill_handler(int sig, int code, struct sigcontext *scp)
1054 {
1055 struct sigstate *state = (struct sigstate *)scp->sc_ap;
1056 uint16 *pc = (uint16 *)scp->sc_pc;
1057 uint16 opcode = *pc;
1058 M68kRegisters *regs = (M68kRegisters *)&state->ss_frame;
1059
1060 #define INC_PC(n) scp->sc_pc += (n)
1061
1062 #define GET_SR (scp->sc_ps | EmulatedSR)
1063
1064 #define STORE_SR(v) \
1065 scp->sc_ps = (v) & 0xff; \
1066 EmulatedSR = (v) & 0xe700; \
1067 if (((v) & 0x0700) == 0 && InterruptFlags) \
1068 TriggerInterrupt();
1069
1070 //printf("opcode %04x at %p, sr %04x, emul_sr %04x\n", opcode, pc, scp->sc_ps, EmulatedSR);
1071
1072 if ((opcode & 0xf000) == 0xa000) {
1073
1074 // A-Line instruction, set up A-Line trap frame on stack
1075 uint32 a7 = regs->a[7];
1076 a7 -= 2;
1077 WriteMacInt16(a7, 0x28);
1078 a7 -= 4;
1079 WriteMacInt32(a7, (uint32)pc);
1080 a7 -= 2;
1081 WriteMacInt16(a7, GET_SR);
1082 scp->sc_sp = regs->a[7] = a7;
1083
1084 // Jump to MacOS A-Line handler on return
1085 scp->sc_pc = ReadMacInt32(0x28);
1086
1087 } else if ((opcode & 0xff00) == 0x7100) {
1088
1089 // Extended opcode, push registers on user stack
1090 uint32 a7 = regs->a[7];
1091 a7 -= 4;
1092 WriteMacInt32(a7, (uint32)pc);
1093 a7 -= 2;
1094 WriteMacInt16(a7, scp->sc_ps);
1095 for (int i=7; i>=0; i--) {
1096 a7 -= 4;
1097 WriteMacInt32(a7, regs->a[i]);
1098 }
1099 for (int i=7; i>=0; i--) {
1100 a7 -= 4;
1101 WriteMacInt32(a7, regs->d[i]);
1102 }
1103 scp->sc_sp = regs->a[7] = a7;
1104
1105 // Jump to EmulOp trampoline code on return
1106 scp->sc_pc = (uint32)EmulOpTrampoline;
1107
1108 } else switch (opcode) { // Emulate privileged instructions
1109
1110 case 0x40e7: // move sr,-(sp)
1111 regs->a[7] -= 2;
1112 WriteMacInt16(regs->a[7], GET_SR);
1113 scp->sc_sp = regs->a[7];
1114 INC_PC(2);
1115 break;
1116
1117 case 0x46df: { // move (sp)+,sr
1118 uint16 sr = ReadMacInt16(regs->a[7]);
1119 STORE_SR(sr);
1120 regs->a[7] += 2;
1121 scp->sc_sp = regs->a[7];
1122 INC_PC(2);
1123 break;
1124 }
1125
1126 case 0x007c: { // ori #xxxx,sr
1127 uint16 sr = GET_SR | pc[1];
1128 scp->sc_ps = sr & 0xff; // oring bits into the sr can't enable interrupts, so we don't need to call STORE_SR
1129 EmulatedSR = sr & 0xe700;
1130 INC_PC(4);
1131 break;
1132 }
1133
1134 case 0x027c: { // andi #xxxx,sr
1135 uint16 sr = GET_SR & pc[1];
1136 STORE_SR(sr);
1137 INC_PC(4);
1138 break;
1139 }
1140
1141 case 0x46fc: // move #xxxx,sr
1142 STORE_SR(pc[1]);
1143 INC_PC(4);
1144 break;
1145
1146 case 0x46ef: { // move (xxxx,sp),sr
1147 uint16 sr = ReadMacInt16(regs->a[7] + (int32)(int16)pc[1]);
1148 STORE_SR(sr);
1149 INC_PC(4);
1150 break;
1151 }
1152
1153 case 0x46d8: // move (a0)+,sr
1154 case 0x46d9: { // move (a1)+,sr
1155 uint16 sr = ReadMacInt16(regs->a[opcode & 7]);
1156 STORE_SR(sr);
1157 regs->a[opcode & 7] += 2;
1158 INC_PC(2);
1159 break;
1160 }
1161
1162 case 0x40f8: // move sr,xxxx.w
1163 WriteMacInt16(pc[1], GET_SR);
1164 INC_PC(4);
1165 break;
1166
1167 case 0x40d0: // move sr,(a0)
1168 case 0x40d1: // move sr,(a1)
1169 case 0x40d2: // move sr,(a2)
1170 case 0x40d3: // move sr,(a3)
1171 case 0x40d4: // move sr,(a4)
1172 case 0x40d5: // move sr,(a5)
1173 case 0x40d6: // move sr,(a6)
1174 case 0x40d7: // move sr,(sp)
1175 WriteMacInt16(regs->a[opcode & 7], GET_SR);
1176 INC_PC(2);
1177 break;
1178
1179 case 0x40c0: // move sr,d0
1180 case 0x40c1: // move sr,d1
1181 case 0x40c2: // move sr,d2
1182 case 0x40c3: // move sr,d3
1183 case 0x40c4: // move sr,d4
1184 case 0x40c5: // move sr,d5
1185 case 0x40c6: // move sr,d6
1186 case 0x40c7: // move sr,d7
1187 regs->d[opcode & 7] = GET_SR;
1188 INC_PC(2);
1189 break;
1190
1191 case 0x46c0: // move d0,sr
1192 case 0x46c1: // move d1,sr
1193 case 0x46c2: // move d2,sr
1194 case 0x46c3: // move d3,sr
1195 case 0x46c4: // move d4,sr
1196 case 0x46c5: // move d5,sr
1197 case 0x46c6: // move d6,sr
1198 case 0x46c7: { // move d7,sr
1199 uint16 sr = regs->d[opcode & 7];
1200 STORE_SR(sr);
1201 INC_PC(2);
1202 break;
1203 }
1204
1205 case 0xf327: // fsave -(sp)
1206 regs->a[7] -= 4;
1207 WriteMacInt32(regs->a[7], 0x41000000); // Idle frame
1208 scp->sc_sp = regs->a[7];
1209 INC_PC(2);
1210 break;
1211
1212 case 0xf35f: // frestore (sp)+
1213 regs->a[7] += 4;
1214 scp->sc_sp = regs->a[7];
1215 INC_PC(2);
1216 break;
1217
1218 case 0x4e73: { // rte
1219 uint32 a7 = regs->a[7];
1220 uint16 sr = ReadMacInt16(a7);
1221 a7 += 2;
1222 scp->sc_ps = sr & 0xff;
1223 EmulatedSR = sr & 0xe700;
1224 scp->sc_pc = ReadMacInt32(a7);
1225 a7 += 4;
1226 uint16 format = ReadMacInt16(a7) >> 12;
1227 a7 += 2;
1228 static const int frame_adj[16] = {
1229 0, 0, 4, 4, 8, 0, 0, 52, 50, 12, 24, 84, 16, 0, 0, 0
1230 };
1231 scp->sc_sp = regs->a[7] = a7 + frame_adj[format];
1232 break;
1233 }
1234
1235 case 0x4e7a: // movec cr,x
1236 switch (pc[1]) {
1237 case 0x0002: // movec cacr,d0
1238 regs->d[0] = 0x3111;
1239 break;
1240 case 0x1002: // movec cacr,d1
1241 regs->d[1] = 0x3111;
1242 break;
1243 case 0x0003: // movec tc,d0
1244 case 0x0004: // movec itt0,d0
1245 case 0x0005: // movec itt1,d0
1246 case 0x0006: // movec dtt0,d0
1247 case 0x0007: // movec dtt1,d0
1248 case 0x0806: // movec urp,d0
1249 case 0x0807: // movec srp,d0
1250 regs->d[0] = 0;
1251 break;
1252 case 0x1000: // movec sfc,d1
1253 case 0x1001: // movec dfc,d1
1254 case 0x1003: // movec tc,d1
1255 case 0x1801: // movec vbr,d1
1256 regs->d[1] = 0;
1257 break;
1258 case 0x8801: // movec vbr,a0
1259 regs->a[0] = 0;
1260 break;
1261 case 0x9801: // movec vbr,a1
1262 regs->a[1] = 0;
1263 break;
1264 default:
1265 goto ill;
1266 }
1267 INC_PC(4);
1268 break;
1269
1270 case 0x4e7b: // movec x,cr
1271 switch (pc[1]) {
1272 case 0x1000: // movec d1,sfc
1273 case 0x1001: // movec d1,dfc
1274 case 0x0801: // movec d0,vbr
1275 case 0x1801: // movec d1,vbr
1276 break;
1277 case 0x0002: // movec d0,cacr
1278 case 0x1002: // movec d1,cacr
1279 FlushCodeCache(NULL, 0);
1280 break;
1281 default:
1282 goto ill;
1283 }
1284 INC_PC(4);
1285 break;
1286
1287 case 0xf478: // cpusha dc
1288 case 0xf4f8: // cpusha dc/ic
1289 FlushCodeCache(NULL, 0);
1290 INC_PC(2);
1291 break;
1292
1293 default:
1294 ill: printf("SIGILL num %d, code %d\n", sig, code);
1295 printf(" context %p:\n", scp);
1296 printf(" onstack %08x\n", scp->sc_onstack);
1297 printf(" sp %08x\n", scp->sc_sp);
1298 printf(" fp %08x\n", scp->sc_fp);
1299 printf(" pc %08x\n", scp->sc_pc);
1300 printf(" opcode %04x\n", opcode);
1301 printf(" sr %08x\n", scp->sc_ps);
1302 printf(" state %p:\n", state);
1303 printf(" flags %d\n", state->ss_flags);
1304 for (int i=0; i<8; i++)
1305 printf(" d%d %08x\n", i, state->ss_frame.f_regs[i]);
1306 for (int i=0; i<8; i++)
1307 printf(" a%d %08x\n", i, state->ss_frame.f_regs[i+8]);
1308
1309 VideoQuitFullScreen();
1310 #ifdef ENABLE_MON
1311 char *arg[4] = {"mon", "-m", "-r", NULL};
1312 mon(3, arg);
1313 #endif
1314 QuitEmulator();
1315 break;
1316 }
1317 }
1318 #endif
1319
1320
1321 /*
1322 * Display alert
1323 */
1324
1325 #ifdef ENABLE_GTK
1326 static void dl_destroyed(void)
1327 {
1328 gtk_main_quit();
1329 }
1330
1331 static void dl_quit(GtkWidget *dialog)
1332 {
1333 gtk_widget_destroy(dialog);
1334 }
1335
1336 void display_alert(int title_id, int prefix_id, int button_id, const char *text)
1337 {
1338 char str[256];
1339 sprintf(str, GetString(prefix_id), text);
1340
1341 GtkWidget *dialog = gtk_dialog_new();
1342 gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
1343 gtk_container_border_width(GTK_CONTAINER(dialog), 5);
1344 gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
1345 gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
1346
1347 GtkWidget *label = gtk_label_new(str);
1348 gtk_widget_show(label);
1349 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
1350
1351 GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
1352 gtk_widget_show(button);
1353 gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
1354 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
1355 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1356 gtk_widget_grab_default(button);
1357 gtk_widget_show(dialog);
1358
1359 gtk_main();
1360 }
1361 #endif
1362
1363
1364 /*
1365 * Display error alert
1366 */
1367
1368 void ErrorAlert(const char *text)
1369 {
1370 #ifdef ENABLE_GTK
1371 if (PrefsFindBool("nogui") || x_display == NULL) {
1372 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
1373 return;
1374 }
1375 VideoQuitFullScreen();
1376 display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
1377 #else
1378 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
1379 #endif
1380 }
1381
1382
1383 /*
1384 * Display warning alert
1385 */
1386
1387 void WarningAlert(const char *text)
1388 {
1389 #ifdef ENABLE_GTK
1390 if (PrefsFindBool("nogui") || x_display == NULL) {
1391 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1392 return;
1393 }
1394 display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
1395 #else
1396 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1397 #endif
1398 }
1399
1400
1401 /*
1402 * Display choice alert
1403 */
1404
1405 bool ChoiceAlert(const char *text, const char *pos, const char *neg)
1406 {
1407 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1408 return false; //!!
1409 }