ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.51
Committed: 2002-11-24T20:07:25Z (21 years, 7 months ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-12, nigel-build-13
Changes since 1.50: +15 -5 lines
Log Message:
Add changes from Brian Johnson:
- Update MIPSpro compiler flags
- Only define static variables if they are to be used
- Try to lock the pthreads mutex prior to unlocking/destroying it

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 segmentation faults
361 #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
362 if (PrefsFindBool("ignoresegv"))
363 sigsegv_set_ignore_state(true);
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 sigaddset(&timer_sa.sa_mask, SIG_IRQ);
619 timer_sa.sa_handler = one_tick;
620 timer_sa.sa_flags = SA_ONSTACK | SA_RESTART;
621 if (sigaction(SIGALRM, &timer_sa, NULL) < 0) {
622 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGALRM", strerror(errno));
623 ErrorAlert(str);
624 QuitEmulator();
625 }
626 struct itimerval req;
627 req.it_interval.tv_sec = req.it_value.tv_sec = 0;
628 req.it_interval.tv_usec = req.it_value.tv_usec = 16625;
629 setitimer(ITIMER_REAL, &req, NULL);
630
631 #endif
632
633 #ifdef HAVE_PTHREADS
634 // Start XPRAM watchdog thread
635 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
636 xpram_thread_active = (pthread_create(&xpram_thread, NULL, xpram_func, NULL) == 0);
637 D(bug("XPRAM thread started\n"));
638 #endif
639
640 // Start 68k and jump to ROM boot routine
641 D(bug("Starting emulation...\n"));
642 Start680x0();
643
644 QuitEmulator();
645 return 0;
646 }
647
648
649 /*
650 * Quit emulator
651 */
652
653 void QuitEmulator(void)
654 {
655 D(bug("QuitEmulator\n"));
656
657 #if EMULATED_68K
658 // Exit 680x0 emulation
659 Exit680x0();
660 #endif
661
662 #if defined(HAVE_PTHREADS)
663 // Stop 60Hz thread
664 if (tick_thread_active) {
665 tick_thread_cancel = true;
666 #ifdef HAVE_PTHREAD_CANCEL
667 pthread_cancel(tick_thread);
668 #endif
669 pthread_join(tick_thread, NULL);
670 }
671 #elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
672 // Stop 60Hz timer
673 timer_delete(timer);
674 #else
675 struct itimerval req;
676 req.it_interval.tv_sec = req.it_value.tv_sec = 0;
677 req.it_interval.tv_usec = req.it_value.tv_usec = 0;
678 setitimer(ITIMER_REAL, &req, NULL);
679 #endif
680
681 #ifdef HAVE_PTHREADS
682 // Stop XPRAM watchdog thread
683 if (xpram_thread_active) {
684 xpram_thread_cancel = true;
685 #ifdef HAVE_PTHREAD_CANCEL
686 pthread_cancel(xpram_thread);
687 #endif
688 pthread_join(xpram_thread, NULL);
689 }
690 #endif
691
692 // Deinitialize everything
693 ExitAll();
694
695 // Free ROM/RAM areas
696 if (RAMBaseHost != VM_MAP_FAILED) {
697 vm_release(RAMBaseHost, RAMSize);
698 RAMBaseHost = NULL;
699 }
700 if (ROMBaseHost != VM_MAP_FAILED) {
701 vm_release(ROMBaseHost, 0x100000);
702 ROMBaseHost = NULL;
703 }
704
705 #if USE_SCRATCHMEM_SUBTERFUGE
706 // Delete scratch memory area
707 if (ScratchMem != (uint8 *)VM_MAP_FAILED) {
708 vm_release((void *)(ScratchMem - SCRATCH_MEM_SIZE/2), SCRATCH_MEM_SIZE);
709 ScratchMem = NULL;
710 }
711 #endif
712
713 #if REAL_ADDRESSING
714 // Delete Low Memory area
715 if (lm_area_mapped)
716 vm_release(0, 0x2000);
717 #endif
718
719 // Exit VM wrappers
720 vm_exit();
721
722 // Exit system routines
723 SysExit();
724
725 // Exit preferences
726 PrefsExit();
727
728 // Close X11 server connection
729 if (x_display)
730 XCloseDisplay(x_display);
731
732 exit(0);
733 }
734
735
736 /*
737 * Code was patched, flush caches if neccessary (i.e. when using a real 680x0
738 * or a dynamically recompiling emulator)
739 */
740
741 void FlushCodeCache(void *start, uint32 size)
742 {
743 #if USE_JIT
744 if (UseJIT)
745 flush_icache(-1);
746 #endif
747 #if !EMULATED_68K && defined(__NetBSD__)
748 m68k_sync_icache(start, size);
749 #endif
750 }
751
752
753 /*
754 * SIGINT handler, enters mon
755 */
756
757 #ifdef ENABLE_MON
758 static void sigint_handler(...)
759 {
760 #if EMULATED_68K
761 uaecptr nextpc;
762 extern void m68k_dumpstate(uaecptr *nextpc);
763 m68k_dumpstate(&nextpc);
764 #endif
765 VideoQuitFullScreen();
766 char *arg[4] = {"mon", "-m", "-r", NULL};
767 mon(3, arg);
768 QuitEmulator();
769 }
770 #endif
771
772
773 #ifdef HAVE_PTHREADS
774 /*
775 * Pthread configuration
776 */
777
778 void Set_pthread_attr(pthread_attr_t *attr, int priority)
779 {
780 pthread_attr_init(attr);
781 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
782 // Some of these only work for superuser
783 if (geteuid() == 0) {
784 pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
785 pthread_attr_setschedpolicy(attr, SCHED_FIFO);
786 struct sched_param fifo_param;
787 fifo_param.sched_priority = ((sched_get_priority_min(SCHED_FIFO) +
788 sched_get_priority_max(SCHED_FIFO)) / 2 +
789 priority);
790 pthread_attr_setschedparam(attr, &fifo_param);
791 }
792 if (pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) != 0) {
793 #ifdef PTHREAD_SCOPE_BOUND_NP
794 // If system scope is not available (eg. we're not running
795 // with CAP_SCHED_MGT capability on an SGI box), try bound
796 // scope. It exposes pthread scheduling to the kernel,
797 // without setting realtime priority.
798 pthread_attr_setscope(attr, PTHREAD_SCOPE_BOUND_NP);
799 #endif
800 }
801 #endif
802 }
803 #endif // HAVE_PTHREADS
804
805
806 /*
807 * Mutexes
808 */
809
810 #ifdef HAVE_PTHREADS
811
812 struct B2_mutex {
813 B2_mutex() {
814 pthread_mutexattr_t attr;
815 pthread_mutexattr_init(&attr);
816 // Initialize the mutex for priority inheritance --
817 // required for accurate timing.
818 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
819 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
820 #endif
821 #if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL)
822 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
823 #endif
824 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
825 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
826 #endif
827 pthread_mutex_init(&m, &attr);
828 pthread_mutexattr_destroy(&attr);
829 }
830 ~B2_mutex() {
831 pthread_mutex_trylock(&m); // Make sure it's locked before
832 pthread_mutex_unlock(&m); // unlocking it.
833 pthread_mutex_destroy(&m);
834 }
835 pthread_mutex_t m;
836 };
837
838 B2_mutex *B2_create_mutex(void)
839 {
840 return new B2_mutex;
841 }
842
843 void B2_lock_mutex(B2_mutex *mutex)
844 {
845 pthread_mutex_lock(&mutex->m);
846 }
847
848 void B2_unlock_mutex(B2_mutex *mutex)
849 {
850 pthread_mutex_unlock(&mutex->m);
851 }
852
853 void B2_delete_mutex(B2_mutex *mutex)
854 {
855 delete mutex;
856 }
857
858 #else
859
860 struct B2_mutex {
861 int dummy;
862 };
863
864 B2_mutex *B2_create_mutex(void)
865 {
866 return new B2_mutex;
867 }
868
869 void B2_lock_mutex(B2_mutex *mutex)
870 {
871 }
872
873 void B2_unlock_mutex(B2_mutex *mutex)
874 {
875 }
876
877 void B2_delete_mutex(B2_mutex *mutex)
878 {
879 delete mutex;
880 }
881
882 #endif
883
884
885 /*
886 * Interrupt flags (must be handled atomically!)
887 */
888
889 uint32 InterruptFlags = 0;
890
891 #if EMULATED_68K
892 void SetInterruptFlag(uint32 flag)
893 {
894 LOCK_INTFLAGS;
895 InterruptFlags |= flag;
896 UNLOCK_INTFLAGS;
897 }
898
899 void ClearInterruptFlag(uint32 flag)
900 {
901 LOCK_INTFLAGS;
902 InterruptFlags &= ~flag;
903 UNLOCK_INTFLAGS;
904 }
905 #endif
906
907 #if !EMULATED_68K
908 void TriggerInterrupt(void)
909 {
910 #if defined(HAVE_PTHREADS)
911 pthread_kill(emul_thread, SIG_IRQ);
912 #else
913 raise(SIG_IRQ);
914 #endif
915 }
916
917 void TriggerNMI(void)
918 {
919 // not yet supported
920 }
921 #endif
922
923
924 /*
925 * XPRAM watchdog thread (saves XPRAM every minute)
926 */
927
928 static void xpram_watchdog(void)
929 {
930 if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
931 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
932 SaveXPRAM();
933 }
934 }
935
936 #ifdef HAVE_PTHREADS
937 static void *xpram_func(void *arg)
938 {
939 while (!xpram_thread_cancel) {
940 for (int i=0; i<60 && !xpram_thread_cancel; i++)
941 Delay_usec(999999); // Only wait 1 second so we quit promptly when xpram_thread_cancel becomes true
942 xpram_watchdog();
943 }
944 return NULL;
945 }
946 #endif
947
948
949 /*
950 * 60Hz thread (really 60.15Hz)
951 */
952
953 static void one_second(void)
954 {
955 // Pseudo Mac 1Hz interrupt, update local time
956 WriteMacInt32(0x20c, TimerDateTime());
957
958 SetInterruptFlag(INTFLAG_1HZ);
959 TriggerInterrupt();
960
961 #ifndef HAVE_PTHREADS
962 static int second_counter = 0;
963 if (++second_counter > 60) {
964 second_counter = 0;
965 xpram_watchdog();
966 }
967 #endif
968 }
969
970 static void one_tick(...)
971 {
972 static int tick_counter = 0;
973 if (++tick_counter > 60) {
974 tick_counter = 0;
975 one_second();
976 }
977
978 #ifndef HAVE_PTHREADS
979 // No threads available, perform video refresh and networking from here
980 VideoRefresh();
981 SetInterruptFlag(INTFLAG_ETHER);
982 #endif
983
984 // Trigger 60Hz interrupt
985 if (ROMVersion != ROM_VERSION_CLASSIC || HasMacStarted()) {
986 SetInterruptFlag(INTFLAG_60HZ);
987 TriggerInterrupt();
988 }
989 }
990
991 #ifdef HAVE_PTHREADS
992 static void *tick_func(void *arg)
993 {
994 uint64 start = GetTicks_usec();
995 int64 ticks = 0;
996 uint64 next = GetTicks_usec();
997 while (!tick_thread_cancel) {
998 one_tick();
999 next += 16625;
1000 int64 delay = next - GetTicks_usec();
1001 if (delay > 0)
1002 Delay_usec(delay);
1003 else if (delay < -16625)
1004 next = GetTicks_usec();
1005 ticks++;
1006 }
1007 uint64 end = GetTicks_usec();
1008 D(bug("%Ld ticks in %Ld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
1009 return NULL;
1010 }
1011 #endif
1012
1013
1014 #if !EMULATED_68K
1015 /*
1016 * Virtual 68k interrupt handler
1017 */
1018
1019 static void sigirq_handler(int sig, int code, struct sigcontext *scp)
1020 {
1021 // Interrupts disabled? Then do nothing
1022 if (EmulatedSR & 0x0700)
1023 return;
1024
1025 struct sigstate *state = (struct sigstate *)scp->sc_ap;
1026 M68kRegisters *regs = (M68kRegisters *)&state->ss_frame;
1027
1028 // Set up interrupt frame on stack
1029 uint32 a7 = regs->a[7];
1030 a7 -= 2;
1031 WriteMacInt16(a7, 0x64);
1032 a7 -= 4;
1033 WriteMacInt32(a7, scp->sc_pc);
1034 a7 -= 2;
1035 WriteMacInt16(a7, scp->sc_ps | EmulatedSR);
1036 scp->sc_sp = regs->a[7] = a7;
1037
1038 // Set interrupt level
1039 EmulatedSR |= 0x2100;
1040
1041 // Jump to MacOS interrupt handler on return
1042 scp->sc_pc = ReadMacInt32(0x64);
1043 }
1044
1045
1046 /*
1047 * SIGILL handler, for emulation of privileged instructions and executing
1048 * A-Trap and EMUL_OP opcodes
1049 */
1050
1051 static void sigill_handler(int sig, int code, struct sigcontext *scp)
1052 {
1053 struct sigstate *state = (struct sigstate *)scp->sc_ap;
1054 uint16 *pc = (uint16 *)scp->sc_pc;
1055 uint16 opcode = *pc;
1056 M68kRegisters *regs = (M68kRegisters *)&state->ss_frame;
1057
1058 #define INC_PC(n) scp->sc_pc += (n)
1059
1060 #define GET_SR (scp->sc_ps | EmulatedSR)
1061
1062 #define STORE_SR(v) \
1063 scp->sc_ps = (v) & 0xff; \
1064 EmulatedSR = (v) & 0xe700; \
1065 if (((v) & 0x0700) == 0 && InterruptFlags) \
1066 TriggerInterrupt();
1067
1068 //printf("opcode %04x at %p, sr %04x, emul_sr %04x\n", opcode, pc, scp->sc_ps, EmulatedSR);
1069
1070 if ((opcode & 0xf000) == 0xa000) {
1071
1072 // A-Line instruction, set up A-Line trap frame on stack
1073 uint32 a7 = regs->a[7];
1074 a7 -= 2;
1075 WriteMacInt16(a7, 0x28);
1076 a7 -= 4;
1077 WriteMacInt32(a7, (uint32)pc);
1078 a7 -= 2;
1079 WriteMacInt16(a7, GET_SR);
1080 scp->sc_sp = regs->a[7] = a7;
1081
1082 // Jump to MacOS A-Line handler on return
1083 scp->sc_pc = ReadMacInt32(0x28);
1084
1085 } else if ((opcode & 0xff00) == 0x7100) {
1086
1087 // Extended opcode, push registers on user stack
1088 uint32 a7 = regs->a[7];
1089 a7 -= 4;
1090 WriteMacInt32(a7, (uint32)pc);
1091 a7 -= 2;
1092 WriteMacInt16(a7, scp->sc_ps);
1093 for (int i=7; i>=0; i--) {
1094 a7 -= 4;
1095 WriteMacInt32(a7, regs->a[i]);
1096 }
1097 for (int i=7; i>=0; i--) {
1098 a7 -= 4;
1099 WriteMacInt32(a7, regs->d[i]);
1100 }
1101 scp->sc_sp = regs->a[7] = a7;
1102
1103 // Jump to EmulOp trampoline code on return
1104 scp->sc_pc = (uint32)EmulOpTrampoline;
1105
1106 } else switch (opcode) { // Emulate privileged instructions
1107
1108 case 0x40e7: // move sr,-(sp)
1109 regs->a[7] -= 2;
1110 WriteMacInt16(regs->a[7], GET_SR);
1111 scp->sc_sp = regs->a[7];
1112 INC_PC(2);
1113 break;
1114
1115 case 0x46df: { // move (sp)+,sr
1116 uint16 sr = ReadMacInt16(regs->a[7]);
1117 STORE_SR(sr);
1118 regs->a[7] += 2;
1119 scp->sc_sp = regs->a[7];
1120 INC_PC(2);
1121 break;
1122 }
1123
1124 case 0x007c: { // ori #xxxx,sr
1125 uint16 sr = GET_SR | pc[1];
1126 scp->sc_ps = sr & 0xff; // oring bits into the sr can't enable interrupts, so we don't need to call STORE_SR
1127 EmulatedSR = sr & 0xe700;
1128 INC_PC(4);
1129 break;
1130 }
1131
1132 case 0x027c: { // andi #xxxx,sr
1133 uint16 sr = GET_SR & pc[1];
1134 STORE_SR(sr);
1135 INC_PC(4);
1136 break;
1137 }
1138
1139 case 0x46fc: // move #xxxx,sr
1140 STORE_SR(pc[1]);
1141 INC_PC(4);
1142 break;
1143
1144 case 0x46ef: { // move (xxxx,sp),sr
1145 uint16 sr = ReadMacInt16(regs->a[7] + (int32)(int16)pc[1]);
1146 STORE_SR(sr);
1147 INC_PC(4);
1148 break;
1149 }
1150
1151 case 0x46d8: // move (a0)+,sr
1152 case 0x46d9: { // move (a1)+,sr
1153 uint16 sr = ReadMacInt16(regs->a[opcode & 7]);
1154 STORE_SR(sr);
1155 regs->a[opcode & 7] += 2;
1156 INC_PC(2);
1157 break;
1158 }
1159
1160 case 0x40f8: // move sr,xxxx.w
1161 WriteMacInt16(pc[1], GET_SR);
1162 INC_PC(4);
1163 break;
1164
1165 case 0x40d0: // move sr,(a0)
1166 case 0x40d1: // move sr,(a1)
1167 case 0x40d2: // move sr,(a2)
1168 case 0x40d3: // move sr,(a3)
1169 case 0x40d4: // move sr,(a4)
1170 case 0x40d5: // move sr,(a5)
1171 case 0x40d6: // move sr,(a6)
1172 case 0x40d7: // move sr,(sp)
1173 WriteMacInt16(regs->a[opcode & 7], GET_SR);
1174 INC_PC(2);
1175 break;
1176
1177 case 0x40c0: // move sr,d0
1178 case 0x40c1: // move sr,d1
1179 case 0x40c2: // move sr,d2
1180 case 0x40c3: // move sr,d3
1181 case 0x40c4: // move sr,d4
1182 case 0x40c5: // move sr,d5
1183 case 0x40c6: // move sr,d6
1184 case 0x40c7: // move sr,d7
1185 regs->d[opcode & 7] = GET_SR;
1186 INC_PC(2);
1187 break;
1188
1189 case 0x46c0: // move d0,sr
1190 case 0x46c1: // move d1,sr
1191 case 0x46c2: // move d2,sr
1192 case 0x46c3: // move d3,sr
1193 case 0x46c4: // move d4,sr
1194 case 0x46c5: // move d5,sr
1195 case 0x46c6: // move d6,sr
1196 case 0x46c7: { // move d7,sr
1197 uint16 sr = regs->d[opcode & 7];
1198 STORE_SR(sr);
1199 INC_PC(2);
1200 break;
1201 }
1202
1203 case 0xf327: // fsave -(sp)
1204 regs->a[7] -= 4;
1205 WriteMacInt32(regs->a[7], 0x41000000); // Idle frame
1206 scp->sc_sp = regs->a[7];
1207 INC_PC(2);
1208 break;
1209
1210 case 0xf35f: // frestore (sp)+
1211 regs->a[7] += 4;
1212 scp->sc_sp = regs->a[7];
1213 INC_PC(2);
1214 break;
1215
1216 case 0x4e73: { // rte
1217 uint32 a7 = regs->a[7];
1218 uint16 sr = ReadMacInt16(a7);
1219 a7 += 2;
1220 scp->sc_ps = sr & 0xff;
1221 EmulatedSR = sr & 0xe700;
1222 scp->sc_pc = ReadMacInt32(a7);
1223 a7 += 4;
1224 uint16 format = ReadMacInt16(a7) >> 12;
1225 a7 += 2;
1226 static const int frame_adj[16] = {
1227 0, 0, 4, 4, 8, 0, 0, 52, 50, 12, 24, 84, 16, 0, 0, 0
1228 };
1229 scp->sc_sp = regs->a[7] = a7 + frame_adj[format];
1230 break;
1231 }
1232
1233 case 0x4e7a: // movec cr,x
1234 switch (pc[1]) {
1235 case 0x0002: // movec cacr,d0
1236 regs->d[0] = 0x3111;
1237 break;
1238 case 0x1002: // movec cacr,d1
1239 regs->d[1] = 0x3111;
1240 break;
1241 case 0x0003: // movec tc,d0
1242 case 0x0004: // movec itt0,d0
1243 case 0x0005: // movec itt1,d0
1244 case 0x0006: // movec dtt0,d0
1245 case 0x0007: // movec dtt1,d0
1246 case 0x0806: // movec urp,d0
1247 case 0x0807: // movec srp,d0
1248 regs->d[0] = 0;
1249 break;
1250 case 0x1000: // movec sfc,d1
1251 case 0x1001: // movec dfc,d1
1252 case 0x1003: // movec tc,d1
1253 case 0x1801: // movec vbr,d1
1254 regs->d[1] = 0;
1255 break;
1256 case 0x8801: // movec vbr,a0
1257 regs->a[0] = 0;
1258 break;
1259 case 0x9801: // movec vbr,a1
1260 regs->a[1] = 0;
1261 break;
1262 default:
1263 goto ill;
1264 }
1265 INC_PC(4);
1266 break;
1267
1268 case 0x4e7b: // movec x,cr
1269 switch (pc[1]) {
1270 case 0x1000: // movec d1,sfc
1271 case 0x1001: // movec d1,dfc
1272 case 0x0801: // movec d0,vbr
1273 case 0x1801: // movec d1,vbr
1274 break;
1275 case 0x0002: // movec d0,cacr
1276 case 0x1002: // movec d1,cacr
1277 FlushCodeCache(NULL, 0);
1278 break;
1279 default:
1280 goto ill;
1281 }
1282 INC_PC(4);
1283 break;
1284
1285 case 0xf478: // cpusha dc
1286 case 0xf4f8: // cpusha dc/ic
1287 FlushCodeCache(NULL, 0);
1288 INC_PC(2);
1289 break;
1290
1291 default:
1292 ill: printf("SIGILL num %d, code %d\n", sig, code);
1293 printf(" context %p:\n", scp);
1294 printf(" onstack %08x\n", scp->sc_onstack);
1295 printf(" sp %08x\n", scp->sc_sp);
1296 printf(" fp %08x\n", scp->sc_fp);
1297 printf(" pc %08x\n", scp->sc_pc);
1298 printf(" opcode %04x\n", opcode);
1299 printf(" sr %08x\n", scp->sc_ps);
1300 printf(" state %p:\n", state);
1301 printf(" flags %d\n", state->ss_flags);
1302 for (int i=0; i<8; i++)
1303 printf(" d%d %08x\n", i, state->ss_frame.f_regs[i]);
1304 for (int i=0; i<8; i++)
1305 printf(" a%d %08x\n", i, state->ss_frame.f_regs[i+8]);
1306
1307 VideoQuitFullScreen();
1308 #ifdef ENABLE_MON
1309 char *arg[4] = {"mon", "-m", "-r", NULL};
1310 mon(3, arg);
1311 #endif
1312 QuitEmulator();
1313 break;
1314 }
1315 }
1316 #endif
1317
1318
1319 /*
1320 * Display alert
1321 */
1322
1323 #ifdef ENABLE_GTK
1324 static void dl_destroyed(void)
1325 {
1326 gtk_main_quit();
1327 }
1328
1329 static void dl_quit(GtkWidget *dialog)
1330 {
1331 gtk_widget_destroy(dialog);
1332 }
1333
1334 void display_alert(int title_id, int prefix_id, int button_id, const char *text)
1335 {
1336 char str[256];
1337 sprintf(str, GetString(prefix_id), text);
1338
1339 GtkWidget *dialog = gtk_dialog_new();
1340 gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
1341 gtk_container_border_width(GTK_CONTAINER(dialog), 5);
1342 gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
1343 gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
1344
1345 GtkWidget *label = gtk_label_new(str);
1346 gtk_widget_show(label);
1347 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
1348
1349 GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
1350 gtk_widget_show(button);
1351 gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
1352 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
1353 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1354 gtk_widget_grab_default(button);
1355 gtk_widget_show(dialog);
1356
1357 gtk_main();
1358 }
1359 #endif
1360
1361
1362 /*
1363 * Display error alert
1364 */
1365
1366 void ErrorAlert(const char *text)
1367 {
1368 #ifdef ENABLE_GTK
1369 if (PrefsFindBool("nogui") || x_display == NULL) {
1370 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
1371 return;
1372 }
1373 VideoQuitFullScreen();
1374 display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
1375 #else
1376 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
1377 #endif
1378 }
1379
1380
1381 /*
1382 * Display warning alert
1383 */
1384
1385 void WarningAlert(const char *text)
1386 {
1387 #ifdef ENABLE_GTK
1388 if (PrefsFindBool("nogui") || x_display == NULL) {
1389 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1390 return;
1391 }
1392 display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
1393 #else
1394 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1395 #endif
1396 }
1397
1398
1399 /*
1400 * Display choice alert
1401 */
1402
1403 bool ChoiceAlert(const char *text, const char *pos, const char *neg)
1404 {
1405 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1406 return false; //!!
1407 }