ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.48
Committed: 2002-07-31T16:46:14Z (22 years ago) by cebix
Branch: MAIN
Changes since 1.47: +55 -20 lines
Log Message:
- it is now possible to make the serial drivers pipe their input/output
  to programs by using a '|' followed by a command line as the modem or
  printer port setting (instead of a device name like '/dev/ttyS0')
  [Brian Johnson]
- the option "--config FILE" tells B2 to use a different config file

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