ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/main_unix.cpp
Revision: 1.18
Committed: 2003-12-05T12:36:10Z (20 years, 6 months ago) by gbeauche
Branch: MAIN
Changes since 1.17: +15 -2 lines
Log Message:
Add XLM_ZERO_PAGE globals which points to a read-only page with all bits
set to zero.

File Contents

# Content
1 /*
2 * main_unix.cpp - Emulation core, Unix implementation
3 *
4 * SheepShaver (C) 1997-2002 Christian Bauer and Marc Hellwig
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 /*
22 * NOTES:
23 *
24 * See main_beos.cpp for a description of the three operating modes.
25 *
26 * In addition to that, we have to handle the fact that the MacOS ABI
27 * is slightly different from the SysV ABI used by Linux:
28 * - Stack frames are different (e.g. LR is stored in 8(r1) under
29 * MacOS, but in 4(r1) under Linux)
30 * - There is no TOC under Linux; r2 is free for the user
31 * - r13 is used as a small data pointer under Linux (but appearently
32 * it is not used this way? To be sure, we specify -msdata=none
33 * in the Makefile)
34 * - As there is no TOC, there are also no TVECTs under Linux;
35 * function pointers point directly to the function code
36 * The Execute*() functions have to account for this. Additionally, we
37 * cannot simply call MacOS functions by getting their TVECT and jumping
38 * to it. Such calls are done via the call_macos*() functions in
39 * asm_linux.S that create a MacOS stack frame, load the TOC pointer
40 * and put the arguments into the right registers.
41 *
42 * As on the BeOS, we have to specify an alternate signal stack because
43 * interrupts (and, under Linux, Low Memory accesses) may occur when r1
44 * is pointing to the Kernel Data or to Low Memory. There is one
45 * problem, however, due to the alternate signal stack being global to
46 * all signal handlers. Consider the following scenario:
47 * - The main thread is executing some native PPC MacOS code in
48 * MODE_NATIVE, running on the MacOS stack (somewhere in the Mac RAM).
49 * - A SIGUSR2 interrupt occurs. The kernel switches to the signal
50 * stack and starts executing the SIGUSR2 signal handler.
51 * - The signal handler sees the MODE_NATIVE and calls ppc_interrupt()
52 * to handle a native interrupt.
53 * - ppc_interrupt() sets r1 to point to the Kernel Data and jumps to
54 * the nanokernel.
55 * - The nanokernel accesses a Low Memory global (most likely one of
56 * the XLMs), a SIGSEGV occurs.
57 * - The kernel sees that r1 does not point to the signal stack and
58 * switches to the signal stack again, thus overwriting the data that
59 * the SIGUSR2 handler put there.
60 * The same problem arises when calling ExecutePPC() inside the MODE_EMUL_OP
61 * interrupt handler.
62 *
63 * The solution is to set the signal stack to a second, "extra" stack
64 * inside the SIGUSR2 handler before entering the Nanokernel or calling
65 * ExecutePPC (or any function that might cause a mode switch). The signal
66 * stack is restored before exiting the SIGUSR2 handler.
67 *
68 * TODO:
69 * check if SIGSEGV handler works for all registers (including FP!)
70 */
71
72 #include <unistd.h>
73 #include <fcntl.h>
74 #include <time.h>
75 #include <errno.h>
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #include <pthread.h>
80 #include <sys/mman.h>
81 #include <sys/ipc.h>
82 #include <sys/shm.h>
83 #include <signal.h>
84
85 #include "sysdeps.h"
86 #include "main.h"
87 #include "version.h"
88 #include "prefs.h"
89 #include "prefs_editor.h"
90 #include "cpu_emulation.h"
91 #include "emul_op.h"
92 #include "xlowmem.h"
93 #include "xpram.h"
94 #include "timer.h"
95 #include "adb.h"
96 #include "sony.h"
97 #include "disk.h"
98 #include "cdrom.h"
99 #include "scsi.h"
100 #include "video.h"
101 #include "audio.h"
102 #include "ether.h"
103 #include "serial.h"
104 #include "clip.h"
105 #include "extfs.h"
106 #include "sys.h"
107 #include "macos_util.h"
108 #include "rom_patches.h"
109 #include "user_strings.h"
110 #include "vm_alloc.h"
111 #include "sigsegv.h"
112 #include "thunks.h"
113
114 #define DEBUG 0
115 #include "debug.h"
116
117
118 #include <X11/Xlib.h>
119
120 #ifdef ENABLE_GTK
121 #include <gtk/gtk.h>
122 #endif
123
124 #ifdef ENABLE_XF86_DGA
125 #include <X11/Xlib.h>
126 #include <X11/Xutil.h>
127 #include <X11/extensions/xf86dga.h>
128 #endif
129
130 #ifdef ENABLE_MON
131 #include "mon.h"
132 #endif
133
134
135 // Enable Execute68k() safety checks?
136 #define SAFE_EXEC_68K 0
137
138 // Interrupts in EMUL_OP mode?
139 #define INTERRUPTS_IN_EMUL_OP_MODE 1
140
141 // Interrupts in native mode?
142 #define INTERRUPTS_IN_NATIVE_MODE 1
143
144
145 // Constants
146 const char ROM_FILE_NAME[] = "ROM";
147 const char ROM_FILE_NAME2[] = "Mac OS ROM";
148
149 const uintptr RAM_BASE = 0x20000000; // Base address of RAM
150 const uint32 SIG_STACK_SIZE = 0x10000; // Size of signal stack
151
152
153 #if !EMULATED_PPC
154 // Structure in which registers are saved in a signal handler;
155 // sigcontext->regs points to it
156 // (see arch/ppc/kernel/signal.c)
157 typedef struct {
158 uint32 u[4];
159 } __attribute((aligned(16))) vector128;
160 #include <linux/elf.h>
161
162 struct sigregs {
163 elf_gregset_t gp_regs; // Identical to pt_regs
164 double fp_regs[ELF_NFPREG]; // f0..f31 and fpsrc
165 //more (uninteresting) stuff following here
166 };
167 #endif
168
169
170 // Global variables (exported)
171 #if !EMULATED_PPC
172 void *TOC; // Small data pointer (r13)
173 #endif
174 uint32 RAMBase; // Base address of Mac RAM
175 uint32 RAMSize; // Size of Mac RAM
176 uint32 KernelDataAddr; // Address of Kernel Data
177 uint32 BootGlobsAddr; // Address of BootGlobs structure at top of Mac RAM
178 uint32 PVR; // Theoretical PVR
179 int64 CPUClockSpeed; // Processor clock speed (Hz)
180 int64 BusClockSpeed; // Bus clock speed (Hz)
181
182
183 // Global variables
184 char *x_display_name = NULL; // X11 display name
185 Display *x_display = NULL; // X11 display handle
186
187 static int zero_fd = 0; // FD of /dev/zero
188 static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
189 static int kernel_area = -1; // SHM ID of Kernel Data area
190 static bool rom_area_mapped = false; // Flag: Mac ROM mmap()ped
191 static bool ram_area_mapped = false; // Flag: Mac RAM mmap()ped
192 static KernelData *kernel_data; // Pointer to Kernel Data
193 static EmulatorData *emulator_data;
194
195 static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes
196
197 static bool nvram_thread_active = false; // Flag: NVRAM watchdog installed
198 static pthread_t nvram_thread; // NVRAM watchdog
199 static bool tick_thread_active = false; // Flag: MacOS thread installed
200 static pthread_t tick_thread; // 60Hz thread
201 static pthread_t emul_thread; // MacOS thread
202
203 static bool ready_for_signals = false; // Handler installed, signals can be sent
204 static int64 num_segv = 0; // Number of handled SEGV signals
205
206 static struct sigaction sigusr2_action; // Interrupt signal (of emulator thread)
207 #if !EMULATED_PPC
208 static struct sigaction sigsegv_action; // Data access exception signal (of emulator thread)
209 static struct sigaction sigill_action; // Illegal instruction signal (of emulator thread)
210 static void *sig_stack = NULL; // Stack for signal handlers
211 static void *extra_stack = NULL; // Stack for SIGSEGV inside interrupt handler
212 static bool emul_thread_fatal = false; // Flag: MacOS thread crashed, tick thread shall dump debug output
213 static sigregs sigsegv_regs; // Register dump when crashed
214 #endif
215
216 uintptr SheepMem::zero_page = 0; // Address of ro page filled in with zeros
217 uintptr SheepMem::base = 0x60000000; // Address of SheepShaver data
218 uintptr SheepMem::top = 0; // Top of SheepShaver data (stack like storage)
219
220
221 // Prototypes
222 static void Quit(void);
223 static void *emul_func(void *arg);
224 static void *nvram_func(void *arg);
225 static void *tick_func(void *arg);
226 #if EMULATED_PPC
227 static void sigusr2_handler(int sig);
228 extern void emul_ppc(uint32 start);
229 extern void init_emul_ppc(void);
230 extern void exit_emul_ppc(void);
231 #else
232 static void sigusr2_handler(int sig, sigcontext_struct *sc);
233 static void sigsegv_handler(int sig, sigcontext_struct *sc);
234 static void sigill_handler(int sig, sigcontext_struct *sc);
235 #endif
236
237
238 // From asm_linux.S
239 #if !EMULATED_PPC
240 extern "C" void *get_toc(void);
241 extern "C" void *get_sp(void);
242 extern "C" void flush_icache_range(void *start, void *end);
243 extern "C" void jump_to_rom(uint32 entry, uint32 context);
244 extern "C" void quit_emulator(void);
245 extern "C" void execute_68k(uint32 pc, M68kRegisters *r);
246 extern "C" void ppc_interrupt(uint32 entry, uint32 kernel_data);
247 extern "C" int atomic_add(int *var, int v);
248 extern "C" int atomic_and(int *var, int v);
249 extern "C" int atomic_or(int *var, int v);
250 extern void paranoia_check(void);
251 #endif
252
253
254 #if EMULATED_PPC
255 /*
256 * Atomic operations
257 */
258
259 #if HAVE_SPINLOCKS
260 static spinlock_t atomic_ops_lock = SPIN_LOCK_UNLOCKED;
261 #else
262 #define spin_lock(LOCK)
263 #define spin_unlock(LOCK)
264 #endif
265
266 int atomic_add(int *var, int v)
267 {
268 spin_lock(&atomic_ops_lock);
269 int ret = *var;
270 *var += v;
271 spin_unlock(&atomic_ops_lock);
272 return ret;
273 }
274
275 int atomic_and(int *var, int v)
276 {
277 spin_lock(&atomic_ops_lock);
278 int ret = *var;
279 *var &= v;
280 spin_unlock(&atomic_ops_lock);
281 return ret;
282 }
283
284 int atomic_or(int *var, int v)
285 {
286 spin_lock(&atomic_ops_lock);
287 int ret = *var;
288 *var |= v;
289 spin_unlock(&atomic_ops_lock);
290 return ret;
291 }
292 #endif
293
294
295 /*
296 * Main program
297 */
298
299 static void usage(const char *prg_name)
300 {
301 printf("Usage: %s [OPTION...]\n", prg_name);
302 printf("\nUnix options:\n");
303 printf(" --display STRING\n X display to use\n");
304 PrefsPrintUsage();
305 exit(0);
306 }
307
308 int main(int argc, char **argv)
309 {
310 char str[256];
311 uint32 *boot_globs;
312 int16 i16;
313 int rom_fd;
314 FILE *proc_file;
315 const char *rom_path;
316 uint32 rom_size, actual;
317 uint8 *rom_tmp;
318 time_t now, expire;
319
320 // Initialize variables
321 RAMBase = 0;
322 tzset();
323
324 // Print some info
325 printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
326 printf(" %s\n", GetString(STR_ABOUT_TEXT2));
327
328 #if !EMULATED_PPC
329 // Get TOC pointer
330 TOC = get_toc();
331 #endif
332
333 #ifdef ENABLE_GTK
334 // Init GTK
335 gtk_set_locale();
336 gtk_init(&argc, &argv);
337 #endif
338
339 // Read preferences
340 PrefsInit(argc, argv);
341
342 // Parse command line arguments
343 for (int i=1; i<argc; i++) {
344 if (strcmp(argv[i], "--help") == 0) {
345 usage(argv[0]);
346 } else if (strcmp(argv[i], "--display") == 0) {
347 i++;
348 if (i < argc)
349 x_display_name = strdup(argv[i]);
350 } else if (argv[i][0] == '-') {
351 fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
352 usage(argv[0]);
353 }
354 }
355
356 // Open display
357 x_display = XOpenDisplay(x_display_name);
358 if (x_display == NULL) {
359 char str[256];
360 sprintf(str, GetString(STR_NO_XSERVER_ERR), XDisplayName(x_display_name));
361 ErrorAlert(str);
362 goto quit;
363 }
364
365 #if defined(ENABLE_XF86_DGA) && !defined(ENABLE_MON)
366 // Fork out, so we can return from fullscreen mode when things get ugly
367 XF86DGAForkApp(DefaultScreen(x_display));
368 #endif
369
370 #ifdef ENABLE_MON
371 // Initialize mon
372 mon_init();
373 #endif
374
375 // Get system info
376 PVR = 0x00040000; // Default: 604
377 CPUClockSpeed = 100000000; // Default: 100MHz
378 BusClockSpeed = 100000000; // Default: 100MHz
379 #if !EMULATED_PPC
380 proc_file = fopen("/proc/cpuinfo", "r");
381 if (proc_file) {
382 char line[256];
383 while(fgets(line, 255, proc_file)) {
384 // Read line
385 int len = strlen(line);
386 if (len == 0)
387 continue;
388 line[len-1] = 0;
389
390 // Parse line
391 int i;
392 char value[256];
393 if (sscanf(line, "cpu : %s", value) == 1) {
394 if (strcmp(value, "601") == 0)
395 PVR = 0x00010000;
396 else if (strcmp(value, "603") == 0)
397 PVR = 0x00030000;
398 else if (strcmp(value, "604") == 0)
399 PVR = 0x00040000;
400 else if (strcmp(value, "603e") == 0)
401 PVR = 0x00060000;
402 else if (strcmp(value, "603ev") == 0)
403 PVR = 0x00070000;
404 else if (strcmp(value, "604e") == 0)
405 PVR = 0x00090000;
406 else if (strcmp(value, "604ev5") == 0)
407 PVR = 0x000a0000;
408 else if (strcmp(value, "750") == 0)
409 PVR = 0x00080000;
410 else if (strcmp(value, "821") == 0)
411 PVR = 0x00320000;
412 else if (strcmp(value, "860") == 0)
413 PVR = 0x00500000;
414 else
415 printf("WARNING: Unknown CPU type '%s', assuming 604\n", value);
416 }
417 if (sscanf(line, "clock : %dMHz", &i) == 1)
418 CPUClockSpeed = BusClockSpeed = i * 1000000;
419 }
420 fclose(proc_file);
421 } else {
422 sprintf(str, GetString(STR_PROC_CPUINFO_WARN), strerror(errno));
423 WarningAlert(str);
424 }
425 #endif
426 D(bug("PVR: %08x (assumed)\n", PVR));
427
428 // Init system routines
429 SysInit();
430
431 // Show preferences editor
432 if (!PrefsFindBool("nogui"))
433 if (!PrefsEditor())
434 goto quit;
435
436 #if !EMULATED_PPC
437 // Check some things
438 paranoia_check();
439 #endif
440
441 // Open /dev/zero
442 zero_fd = open("/dev/zero", O_RDWR);
443 if (zero_fd < 0) {
444 sprintf(str, GetString(STR_NO_DEV_ZERO_ERR), strerror(errno));
445 ErrorAlert(str);
446 goto quit;
447 }
448
449 // Create Low Memory area (0x0000..0x3000)
450 if (vm_acquire_fixed((char *)0, 0x3000) < 0) {
451 sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno));
452 ErrorAlert(str);
453 goto quit;
454 }
455 lm_area_mapped = true;
456
457 // Create areas for Kernel Data
458 kernel_area = shmget(IPC_PRIVATE, KERNEL_AREA_SIZE, 0600);
459 if (kernel_area == -1) {
460 sprintf(str, GetString(STR_KD_SHMGET_ERR), strerror(errno));
461 ErrorAlert(str);
462 goto quit;
463 }
464 if (shmat(kernel_area, (void *)KERNEL_DATA_BASE, 0) < 0) {
465 sprintf(str, GetString(STR_KD_SHMAT_ERR), strerror(errno));
466 ErrorAlert(str);
467 goto quit;
468 }
469 if (shmat(kernel_area, (void *)KERNEL_DATA2_BASE, 0) < 0) {
470 sprintf(str, GetString(STR_KD2_SHMAT_ERR), strerror(errno));
471 ErrorAlert(str);
472 goto quit;
473 }
474 kernel_data = (KernelData *)KERNEL_DATA_BASE;
475 emulator_data = &kernel_data->ed;
476 KernelDataAddr = KERNEL_DATA_BASE;
477 D(bug("Kernel Data at %p, Emulator Data at %p\n", kernel_data, emulator_data));
478
479 // Create area for SheepShaver data
480 if (!SheepMem::Init()) {
481 sprintf(str, GetString(STR_SHEEP_MEM_MMAP_ERR), strerror(errno));
482 ErrorAlert(str);
483 goto quit;
484 }
485
486 // Create area for Mac ROM
487 if (vm_acquire_fixed((char *)ROM_BASE, ROM_AREA_SIZE) < 0) {
488 sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno));
489 ErrorAlert(str);
490 goto quit;
491 }
492 #if !EMULATED_PPC || defined(__powerpc__)
493 if (vm_protect((char *)ROM_BASE, ROM_AREA_SIZE, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
494 sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno));
495 ErrorAlert(str);
496 goto quit;
497 }
498 #endif
499 rom_area_mapped = true;
500 D(bug("ROM area at %08x\n", ROM_BASE));
501
502 // Create area for Mac RAM
503 RAMSize = PrefsFindInt32("ramsize");
504 if (RAMSize < 8*1024*1024) {
505 WarningAlert(GetString(STR_SMALL_RAM_WARN));
506 RAMSize = 8*1024*1024;
507 }
508
509 if (vm_acquire_fixed((char *)RAM_BASE, RAMSize) < 0) {
510 sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno));
511 ErrorAlert(str);
512 goto quit;
513 }
514 #if !EMULATED_PPC
515 if (vm_protect((char *)RAM_BASE, RAMSize, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
516 sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno));
517 ErrorAlert(str);
518 goto quit;
519 }
520 #endif
521 RAMBase = RAM_BASE;
522 ram_area_mapped = true;
523 D(bug("RAM area at %08x\n", RAMBase));
524
525 if (RAMBase > ROM_BASE) {
526 ErrorAlert(GetString(STR_RAM_HIGHER_THAN_ROM_ERR));
527 goto quit;
528 }
529
530 // Load Mac ROM
531 rom_path = PrefsFindString("rom");
532 rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY);
533 if (rom_fd < 0) {
534 rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME2, O_RDONLY);
535 if (rom_fd < 0) {
536 ErrorAlert(GetString(STR_NO_ROM_FILE_ERR));
537 goto quit;
538 }
539 }
540 printf(GetString(STR_READING_ROM_FILE));
541 rom_size = lseek(rom_fd, 0, SEEK_END);
542 lseek(rom_fd, 0, SEEK_SET);
543 rom_tmp = new uint8[ROM_SIZE];
544 actual = read(rom_fd, (void *)rom_tmp, ROM_SIZE);
545 close(rom_fd);
546
547 // Decode Mac ROM
548 if (!DecodeROM(rom_tmp, actual)) {
549 if (rom_size != 4*1024*1024) {
550 ErrorAlert(GetString(STR_ROM_SIZE_ERR));
551 goto quit;
552 } else {
553 ErrorAlert(GetString(STR_ROM_FILE_READ_ERR));
554 goto quit;
555 }
556 }
557 delete[] rom_tmp;
558
559 // Load NVRAM
560 XPRAMInit();
561
562 // Set boot volume
563 i16 = PrefsFindInt32("bootdrive");
564 XPRAM[0x1378] = i16 >> 8;
565 XPRAM[0x1379] = i16 & 0xff;
566 i16 = PrefsFindInt32("bootdriver");
567 XPRAM[0x137a] = i16 >> 8;
568 XPRAM[0x137b] = i16 & 0xff;
569
570 // Create BootGlobs at top of Mac memory
571 memset((void *)(RAMBase + RAMSize - 4096), 0, 4096);
572 BootGlobsAddr = RAMBase + RAMSize - 0x1c;
573 boot_globs = (uint32 *)BootGlobsAddr;
574 boot_globs[-5] = htonl(RAMBase + RAMSize); // MemTop
575 boot_globs[0] = htonl(RAMBase); // First RAM bank
576 boot_globs[1] = htonl(RAMSize);
577 boot_globs[2] = htonl((uint32)-1); // End of bank table
578
579 // Init thunks
580 if (!ThunksInit())
581 goto quit;
582
583 // Init drivers
584 SonyInit();
585 DiskInit();
586 CDROMInit();
587 SCSIInit();
588
589 // Init external file system
590 ExtFSInit();
591
592 // Init audio
593 AudioInit();
594
595 // Init network
596 EtherInit();
597
598 // Init serial ports
599 SerialInit();
600
601 // Init Time Manager
602 TimerInit();
603
604 // Init clipboard
605 ClipInit();
606
607 // Init video
608 if (!VideoInit())
609 goto quit;
610
611 // Install ROM patches
612 if (!PatchROM()) {
613 ErrorAlert(GetString(STR_UNSUPPORTED_ROM_TYPE_ERR));
614 goto quit;
615 }
616
617 // Clear caches (as we loaded and patched code) and write protect ROM
618 #if !EMULATED_PPC
619 MakeExecutable(0, (void *)ROM_BASE, ROM_AREA_SIZE);
620 #endif
621 vm_protect((char *)ROM_BASE, ROM_AREA_SIZE, VM_PAGE_READ | VM_PAGE_EXECUTE);
622
623 // Initialize Kernel Data
624 memset(kernel_data, 0, sizeof(KernelData));
625 if (ROMType == ROMTYPE_NEWWORLD) {
626 uintptr of_dev_tree = SheepMem::Reserve(4 * sizeof(uint32));
627 memset((void *)of_dev_tree, 0, 4 * sizeof(uint32));
628 uintptr vector_lookup_tbl = SheepMem::Reserve(128);
629 uintptr vector_mask_tbl = SheepMem::Reserve(64);
630 memset((uint8 *)kernel_data + 0xb80, 0x3d, 0x80);
631 memset((void *)vector_lookup_tbl, 0, 128);
632 memset((void *)vector_mask_tbl, 0, 64);
633 kernel_data->v[0xb80 >> 2] = htonl(ROM_BASE);
634 kernel_data->v[0xb84 >> 2] = htonl(of_dev_tree); // OF device tree base
635 kernel_data->v[0xb90 >> 2] = htonl(vector_lookup_tbl);
636 kernel_data->v[0xb94 >> 2] = htonl(vector_mask_tbl);
637 kernel_data->v[0xb98 >> 2] = htonl(ROM_BASE); // OpenPIC base
638 kernel_data->v[0xbb0 >> 2] = htonl(0); // ADB base
639 kernel_data->v[0xc20 >> 2] = htonl(RAMSize);
640 kernel_data->v[0xc24 >> 2] = htonl(RAMSize);
641 kernel_data->v[0xc30 >> 2] = htonl(RAMSize);
642 kernel_data->v[0xc34 >> 2] = htonl(RAMSize);
643 kernel_data->v[0xc38 >> 2] = htonl(0x00010020);
644 kernel_data->v[0xc3c >> 2] = htonl(0x00200001);
645 kernel_data->v[0xc40 >> 2] = htonl(0x00010000);
646 kernel_data->v[0xc50 >> 2] = htonl(RAMBase);
647 kernel_data->v[0xc54 >> 2] = htonl(RAMSize);
648 kernel_data->v[0xf60 >> 2] = htonl(PVR);
649 kernel_data->v[0xf64 >> 2] = htonl(CPUClockSpeed);
650 kernel_data->v[0xf68 >> 2] = htonl(BusClockSpeed);
651 kernel_data->v[0xf6c >> 2] = htonl(CPUClockSpeed);
652 } else {
653 kernel_data->v[0xc80 >> 2] = htonl(RAMSize);
654 kernel_data->v[0xc84 >> 2] = htonl(RAMSize);
655 kernel_data->v[0xc90 >> 2] = htonl(RAMSize);
656 kernel_data->v[0xc94 >> 2] = htonl(RAMSize);
657 kernel_data->v[0xc98 >> 2] = htonl(0x00010020);
658 kernel_data->v[0xc9c >> 2] = htonl(0x00200001);
659 kernel_data->v[0xca0 >> 2] = htonl(0x00010000);
660 kernel_data->v[0xcb0 >> 2] = htonl(RAMBase);
661 kernel_data->v[0xcb4 >> 2] = htonl(RAMSize);
662 kernel_data->v[0xf80 >> 2] = htonl(PVR);
663 kernel_data->v[0xf84 >> 2] = htonl(CPUClockSpeed);
664 kernel_data->v[0xf88 >> 2] = htonl(BusClockSpeed);
665 kernel_data->v[0xf8c >> 2] = htonl(CPUClockSpeed);
666 }
667
668 // Initialize extra low memory
669 D(bug("Initializing Low Memory...\n"));
670 memset(NULL, 0, 0x3000);
671 WriteMacInt32(XLM_SIGNATURE, FOURCC('B','a','a','h')); // Signature to detect SheepShaver
672 WriteMacInt32(XLM_KERNEL_DATA, KernelDataAddr); // For trap replacement routines
673 WriteMacInt32(XLM_PVR, PVR); // Theoretical PVR
674 WriteMacInt32(XLM_BUS_CLOCK, BusClockSpeed); // For DriverServicesLib patch
675 WriteMacInt16(XLM_EXEC_RETURN_OPCODE, M68K_EXEC_RETURN); // For Execute68k() (RTS from the executed 68k code will jump here and end 68k mode)
676 WriteMacInt32(XLM_ZERO_PAGE, SheepMem::ZeroPage()); // Pointer to read-only page with all bits set to 0
677 #if !EMULATED_PPC
678 WriteMacInt32(XLM_TOC, (uint32)TOC); // TOC pointer of emulator
679 #endif
680 WriteMacInt32(XLM_ETHER_INIT, NativeFunction(NATIVE_ETHER_INIT)); // DLPI ethernet driver functions
681 WriteMacInt32(XLM_ETHER_TERM, NativeFunction(NATIVE_ETHER_TERM));
682 WriteMacInt32(XLM_ETHER_OPEN, NativeFunction(NATIVE_ETHER_OPEN));
683 WriteMacInt32(XLM_ETHER_CLOSE, NativeFunction(NATIVE_ETHER_CLOSE));
684 WriteMacInt32(XLM_ETHER_WPUT, NativeFunction(NATIVE_ETHER_WPUT));
685 WriteMacInt32(XLM_ETHER_RSRV, NativeFunction(NATIVE_ETHER_RSRV));
686 WriteMacInt32(XLM_VIDEO_DOIO, NativeFunction(NATIVE_VIDEO_DO_DRIVER_IO));
687 D(bug("Low Memory initialized\n"));
688
689 // Start 60Hz thread
690 tick_thread_active = (pthread_create(&tick_thread, NULL, tick_func, NULL) == 0);
691 D(bug("Tick thread installed (%ld)\n", tick_thread));
692
693 // Start NVRAM watchdog thread
694 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
695 nvram_thread_active = (pthread_create(&nvram_thread, NULL, nvram_func, NULL) == 0);
696 D(bug("NVRAM thread installed (%ld)\n", nvram_thread));
697
698 #if !EMULATED_PPC
699 // Create and install stacks for signal handlers
700 sig_stack = malloc(SIG_STACK_SIZE);
701 D(bug("Signal stack at %p\n", sig_stack));
702 if (sig_stack == NULL) {
703 ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR));
704 goto quit;
705 }
706 extra_stack = malloc(SIG_STACK_SIZE);
707 D(bug("Extra stack at %p\n", extra_stack));
708 if (extra_stack == NULL) {
709 ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR));
710 goto quit;
711 }
712 struct sigaltstack new_stack;
713 new_stack.ss_sp = sig_stack;
714 new_stack.ss_flags = 0;
715 new_stack.ss_size = SIG_STACK_SIZE;
716 if (sigaltstack(&new_stack, NULL) < 0) {
717 sprintf(str, GetString(STR_SIGALTSTACK_ERR), strerror(errno));
718 ErrorAlert(str);
719 goto quit;
720 }
721 #endif
722
723 #if !EMULATED_PPC
724 // Install SIGSEGV handler
725 sigemptyset(&sigsegv_action.sa_mask); // Block interrupts during SEGV handling
726 sigaddset(&sigsegv_action.sa_mask, SIGUSR2);
727 sigsegv_action.sa_handler = (__sighandler_t)sigsegv_handler;
728 sigsegv_action.sa_flags = SA_ONSTACK;
729 sigsegv_action.sa_restorer = NULL;
730 if (sigaction(SIGSEGV, &sigsegv_action, NULL) < 0) {
731 sprintf(str, GetString(STR_SIGSEGV_INSTALL_ERR), strerror(errno));
732 ErrorAlert(str);
733 goto quit;
734 }
735
736 // Install SIGILL handler
737 sigemptyset(&sigill_action.sa_mask); // Block interrupts during ILL handling
738 sigaddset(&sigill_action.sa_mask, SIGUSR2);
739 sigill_action.sa_handler = (__sighandler_t)sigill_handler;
740 sigill_action.sa_flags = SA_ONSTACK;
741 sigill_action.sa_restorer = NULL;
742 if (sigaction(SIGILL, &sigill_action, NULL) < 0) {
743 sprintf(str, GetString(STR_SIGILL_INSTALL_ERR), strerror(errno));
744 ErrorAlert(str);
745 goto quit;
746 }
747 #endif
748
749 // Install interrupt signal handler
750 sigemptyset(&sigusr2_action.sa_mask);
751 sigusr2_action.sa_handler = (__sighandler_t)sigusr2_handler;
752 sigusr2_action.sa_flags = 0;
753 #if !EMULATED_PPC
754 sigusr2_action.sa_flags = SA_ONSTACK | SA_RESTART;
755 #endif
756 sigusr2_action.sa_restorer = NULL;
757 if (sigaction(SIGUSR2, &sigusr2_action, NULL) < 0) {
758 sprintf(str, GetString(STR_SIGUSR2_INSTALL_ERR), strerror(errno));
759 ErrorAlert(str);
760 goto quit;
761 }
762
763 // Get my thread ID and execute MacOS thread function
764 emul_thread = pthread_self();
765 D(bug("MacOS thread is %ld\n", emul_thread));
766 emul_func(NULL);
767
768 quit:
769 Quit();
770 return 0;
771 }
772
773
774 /*
775 * Cleanup and quit
776 */
777
778 static void Quit(void)
779 {
780 #if EMULATED_PPC
781 // Exit PowerPC emulation
782 exit_emul_ppc();
783 #endif
784
785 // Stop 60Hz thread
786 if (tick_thread_active) {
787 pthread_cancel(tick_thread);
788 pthread_join(tick_thread, NULL);
789 }
790
791 // Stop NVRAM watchdog thread
792 if (nvram_thread_active) {
793 pthread_cancel(nvram_thread);
794 pthread_join(nvram_thread, NULL);
795 }
796
797 #if !EMULATED_PPC
798 // Uninstall SIGSEGV handler
799 sigemptyset(&sigsegv_action.sa_mask);
800 sigsegv_action.sa_handler = SIG_DFL;
801 sigsegv_action.sa_flags = 0;
802 sigaction(SIGSEGV, &sigsegv_action, NULL);
803
804 // Uninstall SIGILL handler
805 sigemptyset(&sigill_action.sa_mask);
806 sigill_action.sa_handler = SIG_DFL;
807 sigill_action.sa_flags = 0;
808 sigaction(SIGILL, &sigill_action, NULL);
809 #endif
810
811 // Save NVRAM
812 XPRAMExit();
813
814 // Exit clipboard
815 ClipExit();
816
817 // Exit Time Manager
818 TimerExit();
819
820 // Exit serial
821 SerialExit();
822
823 // Exit network
824 EtherExit();
825
826 // Exit audio
827 AudioExit();
828
829 // Exit video
830 VideoExit();
831
832 // Exit external file system
833 ExtFSExit();
834
835 // Exit drivers
836 SCSIExit();
837 CDROMExit();
838 DiskExit();
839 SonyExit();
840
841 // Delete SheepShaver globals
842 SheepMem::Exit();
843
844 // Delete RAM area
845 if (ram_area_mapped)
846 vm_release((char *)RAM_BASE, RAMSize);
847
848 // Delete ROM area
849 if (rom_area_mapped)
850 vm_release((char *)ROM_BASE, ROM_AREA_SIZE);
851
852 // Delete Kernel Data area
853 if (kernel_area >= 0) {
854 shmdt((void *)KERNEL_DATA_BASE);
855 shmdt((void *)KERNEL_DATA2_BASE);
856 shmctl(kernel_area, IPC_RMID, NULL);
857 }
858
859 // Delete Low Memory area
860 if (lm_area_mapped)
861 munmap((char *)0x0000, 0x3000);
862
863 // Close /dev/zero
864 if (zero_fd > 0)
865 close(zero_fd);
866
867 // Exit system routines
868 SysExit();
869
870 // Exit preferences
871 PrefsExit();
872
873 #ifdef ENABLE_MON
874 // Exit mon
875 mon_exit();
876 #endif
877
878 // Close X11 server connection
879 if (x_display)
880 XCloseDisplay(x_display);
881
882 exit(0);
883 }
884
885
886 /*
887 * Jump into Mac ROM, start 680x0 emulator
888 */
889
890 #if EMULATED_PPC
891 void jump_to_rom(uint32 entry)
892 {
893 init_emul_ppc();
894 emul_ppc(entry);
895 }
896 #endif
897
898
899 /*
900 * Emulator thread function
901 */
902
903 static void *emul_func(void *arg)
904 {
905 // We're now ready to receive signals
906 ready_for_signals = true;
907
908 // Decrease priority, so more time-critical things like audio will work better
909 nice(1);
910
911 // Jump to ROM boot routine
912 D(bug("Jumping to ROM\n"));
913 #if EMULATED_PPC
914 jump_to_rom(ROM_BASE + 0x310000);
915 #else
916 jump_to_rom(ROM_BASE + 0x310000, (uint32)emulator_data);
917 #endif
918 D(bug("Returned from ROM\n"));
919
920 // We're no longer ready to receive signals
921 ready_for_signals = false;
922 return NULL;
923 }
924
925
926 #if !EMULATED_PPC
927 /*
928 * Execute 68k subroutine (must be ended with RTS)
929 * This must only be called by the emul_thread when in EMUL_OP mode
930 * r->a[7] is unused, the routine runs on the caller's stack
931 */
932
933 void Execute68k(uint32 pc, M68kRegisters *r)
934 {
935 #if SAFE_EXEC_68K
936 if (ReadMacInt32(XLM_RUN_MODE) != MODE_EMUL_OP)
937 printf("FATAL: Execute68k() not called from EMUL_OP mode\n");
938 if (!pthread_equal(pthread_self(), emul_thread))
939 printf("FATAL: Execute68k() not called from emul_thread\n");
940 #endif
941 execute_68k(pc, r);
942 }
943
944
945 /*
946 * Execute 68k A-Trap from EMUL_OP routine
947 * r->a[7] is unused, the routine runs on the caller's stack
948 */
949
950 void Execute68kTrap(uint16 trap, M68kRegisters *r)
951 {
952 uint16 proc[2] = {trap, M68K_RTS};
953 Execute68k((uint32)proc, r);
954 }
955 #endif
956
957
958 /*
959 * Quit emulator (cause return from jump_to_rom)
960 */
961
962 void QuitEmulator(void)
963 {
964 #if EMULATED_PPC
965 Quit();
966 #else
967 quit_emulator();
968 #endif
969 }
970
971
972 /*
973 * Pause/resume emulator
974 */
975
976 void PauseEmulator(void)
977 {
978 pthread_kill(emul_thread, SIGSTOP);
979 }
980
981 void ResumeEmulator(void)
982 {
983 pthread_kill(emul_thread, SIGCONT);
984 }
985
986
987 /*
988 * Dump 68k registers
989 */
990
991 void Dump68kRegs(M68kRegisters *r)
992 {
993 // Display 68k registers
994 for (int i=0; i<8; i++) {
995 printf("d%d: %08x", i, r->d[i]);
996 if (i == 3 || i == 7)
997 printf("\n");
998 else
999 printf(", ");
1000 }
1001 for (int i=0; i<8; i++) {
1002 printf("a%d: %08x", i, r->a[i]);
1003 if (i == 3 || i == 7)
1004 printf("\n");
1005 else
1006 printf(", ");
1007 }
1008 }
1009
1010
1011 /*
1012 * Make code executable
1013 */
1014
1015 void MakeExecutable(int dummy, void *start, uint32 length)
1016 {
1017 if (((uintptr)start >= ROM_BASE) && ((uintptr)start < (ROM_BASE + ROM_SIZE)))
1018 return;
1019 #if EMULATED_PPC
1020 FlushCodeCache((uintptr)start, (uintptr)start + length);
1021 #else
1022 flush_icache_range(start, (void *)((uintptr)start + length));
1023 #endif
1024 }
1025
1026
1027 /*
1028 * Patch things after system startup (gets called by disk driver accRun routine)
1029 */
1030
1031 void PatchAfterStartup(void)
1032 {
1033 ExecuteNative(NATIVE_VIDEO_INSTALL_ACCEL);
1034 InstallExtFS();
1035 }
1036
1037
1038 /*
1039 * NVRAM watchdog thread (saves NVRAM every minute)
1040 */
1041
1042 static void *nvram_func(void *arg)
1043 {
1044 struct timespec req = {60, 0}; // 1 minute
1045
1046 for (;;) {
1047 pthread_testcancel();
1048 nanosleep(&req, NULL);
1049 pthread_testcancel();
1050 if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
1051 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
1052 SaveXPRAM();
1053 }
1054 }
1055 return NULL;
1056 }
1057
1058
1059 /*
1060 * 60Hz thread (really 60.15Hz)
1061 */
1062
1063 static void *tick_func(void *arg)
1064 {
1065 int tick_counter = 0;
1066 struct timespec req = {0, 16625000};
1067
1068 for (;;) {
1069
1070 // Wait
1071 nanosleep(&req, NULL);
1072
1073 #if !EMULATED_PPC
1074 // Did we crash?
1075 if (emul_thread_fatal) {
1076
1077 // Yes, dump registers
1078 pt_regs *r = (pt_regs *)&sigsegv_regs;
1079 char str[256];
1080 sprintf(str, "SIGSEGV\n"
1081 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
1082 " xer %08lx cr %08lx \n"
1083 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
1084 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
1085 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
1086 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
1087 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
1088 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
1089 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
1090 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
1091 r->nip, r->link, r->ctr, r->msr,
1092 r->xer, r->ccr,
1093 r->gpr[0], r->gpr[1], r->gpr[2], r->gpr[3],
1094 r->gpr[4], r->gpr[5], r->gpr[6], r->gpr[7],
1095 r->gpr[8], r->gpr[9], r->gpr[10], r->gpr[11],
1096 r->gpr[12], r->gpr[13], r->gpr[14], r->gpr[15],
1097 r->gpr[16], r->gpr[17], r->gpr[18], r->gpr[19],
1098 r->gpr[20], r->gpr[21], r->gpr[22], r->gpr[23],
1099 r->gpr[24], r->gpr[25], r->gpr[26], r->gpr[27],
1100 r->gpr[28], r->gpr[29], r->gpr[30], r->gpr[31]);
1101 printf(str);
1102 VideoQuitFullScreen();
1103
1104 #ifdef ENABLE_MON
1105 // Start up mon in real-mode
1106 printf("Welcome to the sheep factory.\n");
1107 char *arg[4] = {"mon", "-m", "-r", NULL};
1108 mon(3, arg);
1109 #endif
1110 return NULL;
1111 }
1112 #endif
1113
1114 // Pseudo Mac 1Hz interrupt, update local time
1115 if (++tick_counter > 60) {
1116 tick_counter = 0;
1117 WriteMacInt32(0x20c, TimerDateTime());
1118 }
1119
1120 // Trigger 60Hz interrupt
1121 if (ReadMacInt32(XLM_IRQ_NEST) == 0) {
1122 SetInterruptFlag(INTFLAG_VIA);
1123 TriggerInterrupt();
1124 }
1125 }
1126 return NULL;
1127 }
1128
1129
1130 /*
1131 * Pthread configuration
1132 */
1133
1134 void Set_pthread_attr(pthread_attr_t *attr, int priority)
1135 {
1136 #ifdef HAVE_PTHREADS
1137 pthread_attr_init(attr);
1138 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
1139 // Some of these only work for superuser
1140 if (geteuid() == 0) {
1141 pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
1142 pthread_attr_setschedpolicy(attr, SCHED_FIFO);
1143 struct sched_param fifo_param;
1144 fifo_param.sched_priority = ((sched_get_priority_min(SCHED_FIFO) +
1145 sched_get_priority_max(SCHED_FIFO)) / 2 +
1146 priority);
1147 pthread_attr_setschedparam(attr, &fifo_param);
1148 }
1149 if (pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) != 0) {
1150 #ifdef PTHREAD_SCOPE_BOUND_NP
1151 // If system scope is not available (eg. we're not running
1152 // with CAP_SCHED_MGT capability on an SGI box), try bound
1153 // scope. It exposes pthread scheduling to the kernel,
1154 // without setting realtime priority.
1155 pthread_attr_setscope(attr, PTHREAD_SCOPE_BOUND_NP);
1156 #endif
1157 }
1158 #endif
1159 #endif
1160 }
1161
1162
1163 /*
1164 * Mutexes
1165 */
1166
1167 #ifdef HAVE_PTHREADS
1168
1169 struct B2_mutex {
1170 B2_mutex() {
1171 pthread_mutexattr_t attr;
1172 pthread_mutexattr_init(&attr);
1173 // Initialize the mutex for priority inheritance --
1174 // required for accurate timing.
1175 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
1176 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
1177 #endif
1178 #if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL)
1179 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
1180 #endif
1181 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
1182 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
1183 #endif
1184 pthread_mutex_init(&m, &attr);
1185 pthread_mutexattr_destroy(&attr);
1186 }
1187 ~B2_mutex() {
1188 pthread_mutex_trylock(&m); // Make sure it's locked before
1189 pthread_mutex_unlock(&m); // unlocking it.
1190 pthread_mutex_destroy(&m);
1191 }
1192 pthread_mutex_t m;
1193 };
1194
1195 B2_mutex *B2_create_mutex(void)
1196 {
1197 return new B2_mutex;
1198 }
1199
1200 void B2_lock_mutex(B2_mutex *mutex)
1201 {
1202 pthread_mutex_lock(&mutex->m);
1203 }
1204
1205 void B2_unlock_mutex(B2_mutex *mutex)
1206 {
1207 pthread_mutex_unlock(&mutex->m);
1208 }
1209
1210 void B2_delete_mutex(B2_mutex *mutex)
1211 {
1212 delete mutex;
1213 }
1214
1215 #else
1216
1217 struct B2_mutex {
1218 int dummy;
1219 };
1220
1221 B2_mutex *B2_create_mutex(void)
1222 {
1223 return new B2_mutex;
1224 }
1225
1226 void B2_lock_mutex(B2_mutex *mutex)
1227 {
1228 }
1229
1230 void B2_unlock_mutex(B2_mutex *mutex)
1231 {
1232 }
1233
1234 void B2_delete_mutex(B2_mutex *mutex)
1235 {
1236 delete mutex;
1237 }
1238
1239 #endif
1240
1241
1242 /*
1243 * Trigger signal USR2 from another thread
1244 */
1245
1246 #if !EMULATED_PPC || ASYNC_IRQ
1247 void TriggerInterrupt(void)
1248 {
1249 if (ready_for_signals)
1250 pthread_kill(emul_thread, SIGUSR2);
1251 }
1252 #endif
1253
1254
1255 /*
1256 * Interrupt flags (must be handled atomically!)
1257 */
1258
1259 volatile uint32 InterruptFlags = 0;
1260
1261 void SetInterruptFlag(uint32 flag)
1262 {
1263 atomic_or((int *)&InterruptFlags, flag);
1264 }
1265
1266 void ClearInterruptFlag(uint32 flag)
1267 {
1268 atomic_and((int *)&InterruptFlags, ~flag);
1269 }
1270
1271
1272 /*
1273 * Disable interrupts
1274 */
1275
1276 void DisableInterrupt(void)
1277 {
1278 atomic_add((int *)XLM_IRQ_NEST, 1);
1279 }
1280
1281
1282 /*
1283 * Enable interrupts
1284 */
1285
1286 void EnableInterrupt(void)
1287 {
1288 atomic_add((int *)XLM_IRQ_NEST, -1);
1289 }
1290
1291
1292 /*
1293 * USR2 handler
1294 */
1295
1296 #if EMULATED_PPC
1297 static void sigusr2_handler(int sig)
1298 {
1299 #if ASYNC_IRQ
1300 extern void HandleInterrupt(void);
1301 HandleInterrupt();
1302 #endif
1303 }
1304 #else
1305 static void sigusr2_handler(int sig, sigcontext_struct *sc)
1306 {
1307 pt_regs *r = sc->regs;
1308
1309 // Do nothing if interrupts are disabled
1310 if (*(int32 *)XLM_IRQ_NEST > 0)
1311 return;
1312
1313 // Disable MacOS stack sniffer
1314 WriteMacInt32(0x110, 0);
1315
1316 // Interrupt action depends on current run mode
1317 switch (ReadMacInt32(XLM_RUN_MODE)) {
1318 case MODE_68K:
1319 // 68k emulator active, trigger 68k interrupt level 1
1320 WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1);
1321 r->ccr |= ntohl(kernel_data->v[0x674 >> 2]);
1322 break;
1323
1324 #if INTERRUPTS_IN_NATIVE_MODE
1325 case MODE_NATIVE:
1326 // 68k emulator inactive, in nanokernel?
1327 if (r->gpr[1] != KernelDataAddr) {
1328 // Prepare for 68k interrupt level 1
1329 WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1);
1330 WriteMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc, ReadMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc) | ntohl(kernel_data->v[0x674 >> 2]));
1331
1332 // Execute nanokernel interrupt routine (this will activate the 68k emulator)
1333 atomic_add((int32 *)XLM_IRQ_NEST, 1);
1334 if (ROMType == ROMTYPE_NEWWORLD)
1335 ppc_interrupt(ROM_BASE + 0x312b1c, KernelDataAddr);
1336 else
1337 ppc_interrupt(ROM_BASE + 0x312a3c, KernelDataAddr);
1338 }
1339 break;
1340 #endif
1341
1342 #if INTERRUPTS_IN_EMUL_OP_MODE
1343 case MODE_EMUL_OP:
1344 // 68k emulator active, within EMUL_OP routine, execute 68k interrupt routine directly when interrupt level is 0
1345 if ((ReadMacInt32(XLM_68K_R25) & 7) == 0) {
1346
1347 // Set extra stack for SIGSEGV handler
1348 struct sigaltstack new_stack;
1349 new_stack.ss_sp = extra_stack;
1350 new_stack.ss_flags = 0;
1351 new_stack.ss_size = SIG_STACK_SIZE;
1352 sigaltstack(&new_stack, NULL);
1353 #if 1
1354 // Execute full 68k interrupt routine
1355 M68kRegisters r;
1356 uint32 old_r25 = ReadMacInt32(XLM_68K_R25); // Save interrupt level
1357 WriteMacInt32(XLM_68K_R25, 0x21); // Execute with interrupt level 1
1358 static const uint16 proc[] = {
1359 0x3f3c, 0x0000, // move.w #$0000,-(sp) (fake format word)
1360 0x487a, 0x000a, // pea @1(pc) (return address)
1361 0x40e7, // move sr,-(sp) (saved SR)
1362 0x2078, 0x0064, // move.l $64,a0
1363 0x4ed0, // jmp (a0)
1364 M68K_RTS // @1
1365 };
1366 Execute68k((uint32)proc, &r);
1367 WriteMacInt32(XLM_68K_R25, old_r25); // Restore interrupt level
1368 #else
1369 // Only update cursor
1370 if (HasMacStarted()) {
1371 if (InterruptFlags & INTFLAG_VIA) {
1372 ClearInterruptFlag(INTFLAG_VIA);
1373 ADBInterrupt();
1374 ExecuteNative(NATIVE_VIDEO_VBL);
1375 }
1376 }
1377 #endif
1378 // Reset normal signal stack
1379 new_stack.ss_sp = sig_stack;
1380 new_stack.ss_flags = 0;
1381 new_stack.ss_size = SIG_STACK_SIZE;
1382 sigaltstack(&new_stack, NULL);
1383 }
1384 break;
1385 #endif
1386 }
1387 }
1388 #endif
1389
1390
1391 /*
1392 * SIGSEGV handler
1393 */
1394
1395 #if !EMULATED_PPC
1396 static void sigsegv_handler(int sig, sigcontext_struct *sc)
1397 {
1398 pt_regs *r = sc->regs;
1399
1400 // Get effective address
1401 uint32 addr = r->dar;
1402
1403 #if ENABLE_VOSF
1404 // Handle screen fault.
1405 extern bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction);
1406 if (Screen_fault_handler((sigsegv_address_t)addr, (sigsegv_address_t)r->nip))
1407 return;
1408 #endif
1409
1410 num_segv++;
1411
1412 // Fault in Mac ROM or RAM?
1413 bool mac_fault = (r->nip >= ROM_BASE) && (r->nip < (ROM_BASE + ROM_AREA_SIZE)) || (r->nip >= RAMBase) && (r->nip < (RAMBase + RAMSize));
1414 if (mac_fault) {
1415
1416 // "VM settings" during MacOS 8 installation
1417 if (r->nip == ROM_BASE + 0x488160 && r->gpr[20] == 0xf8000000) {
1418 r->nip += 4;
1419 r->gpr[8] = 0;
1420 return;
1421
1422 // MacOS 8.5 installation
1423 } else if (r->nip == ROM_BASE + 0x488140 && r->gpr[16] == 0xf8000000) {
1424 r->nip += 4;
1425 r->gpr[8] = 0;
1426 return;
1427
1428 // MacOS 8 serial drivers on startup
1429 } else if (r->nip == ROM_BASE + 0x48e080 && (r->gpr[8] == 0xf3012002 || r->gpr[8] == 0xf3012000)) {
1430 r->nip += 4;
1431 r->gpr[8] = 0;
1432 return;
1433
1434 // MacOS 8.1 serial drivers on startup
1435 } else if (r->nip == ROM_BASE + 0x48c5e0 && (r->gpr[20] == 0xf3012002 || r->gpr[20] == 0xf3012000)) {
1436 r->nip += 4;
1437 return;
1438 } else if (r->nip == ROM_BASE + 0x4a10a0 && (r->gpr[20] == 0xf3012002 || r->gpr[20] == 0xf3012000)) {
1439 r->nip += 4;
1440 return;
1441 }
1442
1443 // Get opcode and divide into fields
1444 uint32 opcode = *((uint32 *)r->nip);
1445 uint32 primop = opcode >> 26;
1446 uint32 exop = (opcode >> 1) & 0x3ff;
1447 uint32 ra = (opcode >> 16) & 0x1f;
1448 uint32 rb = (opcode >> 11) & 0x1f;
1449 uint32 rd = (opcode >> 21) & 0x1f;
1450 int32 imm = (int16)(opcode & 0xffff);
1451
1452 // Analyze opcode
1453 enum {
1454 TYPE_UNKNOWN,
1455 TYPE_LOAD,
1456 TYPE_STORE
1457 } transfer_type = TYPE_UNKNOWN;
1458 enum {
1459 SIZE_UNKNOWN,
1460 SIZE_BYTE,
1461 SIZE_HALFWORD,
1462 SIZE_WORD
1463 } transfer_size = SIZE_UNKNOWN;
1464 enum {
1465 MODE_UNKNOWN,
1466 MODE_NORM,
1467 MODE_U,
1468 MODE_X,
1469 MODE_UX
1470 } addr_mode = MODE_UNKNOWN;
1471 switch (primop) {
1472 case 31:
1473 switch (exop) {
1474 case 23: // lwzx
1475 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1476 case 55: // lwzux
1477 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1478 case 87: // lbzx
1479 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1480 case 119: // lbzux
1481 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1482 case 151: // stwx
1483 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1484 case 183: // stwux
1485 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1486 case 215: // stbx
1487 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1488 case 247: // stbux
1489 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1490 case 279: // lhzx
1491 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1492 case 311: // lhzux
1493 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1494 case 343: // lhax
1495 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1496 case 375: // lhaux
1497 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1498 case 407: // sthx
1499 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1500 case 439: // sthux
1501 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1502 }
1503 break;
1504
1505 case 32: // lwz
1506 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1507 case 33: // lwzu
1508 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
1509 case 34: // lbz
1510 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
1511 case 35: // lbzu
1512 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
1513 case 36: // stw
1514 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1515 case 37: // stwu
1516 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
1517 case 38: // stb
1518 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
1519 case 39: // stbu
1520 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
1521 case 40: // lhz
1522 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1523 case 41: // lhzu
1524 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1525 case 42: // lha
1526 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1527 case 43: // lhau
1528 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1529 case 44: // sth
1530 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1531 case 45: // sthu
1532 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1533 }
1534
1535 // Ignore ROM writes
1536 if (transfer_type == TYPE_STORE && addr >= ROM_BASE && addr < ROM_BASE + ROM_SIZE) {
1537 // D(bug("WARNING: %s write access to ROM at %08lx, pc %08lx\n", transfer_size == SIZE_BYTE ? "Byte" : transfer_size == SIZE_HALFWORD ? "Halfword" : "Word", addr, r->nip));
1538 if (addr_mode == MODE_U || addr_mode == MODE_UX)
1539 r->gpr[ra] = addr;
1540 r->nip += 4;
1541 goto rti;
1542 }
1543
1544 // Ignore illegal memory accesses?
1545 if (PrefsFindBool("ignoresegv")) {
1546 if (addr_mode == MODE_U || addr_mode == MODE_UX)
1547 r->gpr[ra] = addr;
1548 if (transfer_type == TYPE_LOAD)
1549 r->gpr[rd] = 0;
1550 r->nip += 4;
1551 goto rti;
1552 }
1553
1554 // In GUI mode, show error alert
1555 if (!PrefsFindBool("nogui")) {
1556 char str[256];
1557 if (transfer_type == TYPE_LOAD || transfer_type == TYPE_STORE)
1558 sprintf(str, GetString(STR_MEM_ACCESS_ERR), transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_HALFWORD ? "halfword" : "word", transfer_type == TYPE_LOAD ? GetString(STR_MEM_ACCESS_READ) : GetString(STR_MEM_ACCESS_WRITE), addr, r->nip, r->gpr[24], r->gpr[1]);
1559 else
1560 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->nip, r->gpr[24], r->gpr[1], opcode);
1561 ErrorAlert(str);
1562 QuitEmulator();
1563 return;
1564 }
1565 }
1566
1567 // For all other errors, jump into debugger (sort of...)
1568 if (!ready_for_signals) {
1569 printf("SIGSEGV\n");
1570 printf(" sigcontext %p, pt_regs %p\n", sc, r);
1571 printf(
1572 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
1573 " xer %08lx cr %08lx \n"
1574 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
1575 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
1576 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
1577 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
1578 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
1579 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
1580 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
1581 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
1582 r->nip, r->link, r->ctr, r->msr,
1583 r->xer, r->ccr,
1584 r->gpr[0], r->gpr[1], r->gpr[2], r->gpr[3],
1585 r->gpr[4], r->gpr[5], r->gpr[6], r->gpr[7],
1586 r->gpr[8], r->gpr[9], r->gpr[10], r->gpr[11],
1587 r->gpr[12], r->gpr[13], r->gpr[14], r->gpr[15],
1588 r->gpr[16], r->gpr[17], r->gpr[18], r->gpr[19],
1589 r->gpr[20], r->gpr[21], r->gpr[22], r->gpr[23],
1590 r->gpr[24], r->gpr[25], r->gpr[26], r->gpr[27],
1591 r->gpr[28], r->gpr[29], r->gpr[30], r->gpr[31]);
1592 exit(1);
1593 QuitEmulator();
1594 return;
1595 } else {
1596 // We crashed. Save registers, tell tick thread and loop forever
1597 sigsegv_regs = *(sigregs *)r;
1598 emul_thread_fatal = true;
1599 for (;;) ;
1600 }
1601 rti:;
1602 }
1603
1604
1605 /*
1606 * SIGILL handler
1607 */
1608
1609 static void sigill_handler(int sig, sigcontext_struct *sc)
1610 {
1611 pt_regs *r = sc->regs;
1612 char str[256];
1613
1614 // Fault in Mac ROM or RAM?
1615 bool mac_fault = (r->nip >= ROM_BASE) && (r->nip < (ROM_BASE + ROM_AREA_SIZE)) || (r->nip >= RAMBase) && (r->nip < (RAMBase + RAMSize));
1616 if (mac_fault) {
1617
1618 // Get opcode and divide into fields
1619 uint32 opcode = *((uint32 *)r->nip);
1620 uint32 primop = opcode >> 26;
1621 uint32 exop = (opcode >> 1) & 0x3ff;
1622 uint32 ra = (opcode >> 16) & 0x1f;
1623 uint32 rb = (opcode >> 11) & 0x1f;
1624 uint32 rd = (opcode >> 21) & 0x1f;
1625 int32 imm = (int16)(opcode & 0xffff);
1626
1627 switch (primop) {
1628 case 9: // POWER instructions
1629 case 22:
1630 power_inst: sprintf(str, GetString(STR_POWER_INSTRUCTION_ERR), r->nip, r->gpr[1], opcode);
1631 ErrorAlert(str);
1632 QuitEmulator();
1633 return;
1634
1635 case 31:
1636 switch (exop) {
1637 case 83: // mfmsr
1638 r->gpr[rd] = 0xf072;
1639 r->nip += 4;
1640 goto rti;
1641
1642 case 210: // mtsr
1643 case 242: // mtsrin
1644 case 306: // tlbie
1645 r->nip += 4;
1646 goto rti;
1647
1648 case 339: { // mfspr
1649 int spr = ra | (rb << 5);
1650 switch (spr) {
1651 case 0: // MQ
1652 case 22: // DEC
1653 case 952: // MMCR0
1654 case 953: // PMC1
1655 case 954: // PMC2
1656 case 955: // SIA
1657 case 956: // MMCR1
1658 case 957: // PMC3
1659 case 958: // PMC4
1660 case 959: // SDA
1661 r->nip += 4;
1662 goto rti;
1663 case 25: // SDR1
1664 r->gpr[rd] = 0xdead001f;
1665 r->nip += 4;
1666 goto rti;
1667 case 287: // PVR
1668 r->gpr[rd] = PVR;
1669 r->nip += 4;
1670 goto rti;
1671 }
1672 break;
1673 }
1674
1675 case 467: { // mtspr
1676 int spr = ra | (rb << 5);
1677 switch (spr) {
1678 case 0: // MQ
1679 case 22: // DEC
1680 case 275: // SPRG3
1681 case 528: // IBAT0U
1682 case 529: // IBAT0L
1683 case 530: // IBAT1U
1684 case 531: // IBAT1L
1685 case 532: // IBAT2U
1686 case 533: // IBAT2L
1687 case 534: // IBAT3U
1688 case 535: // IBAT3L
1689 case 536: // DBAT0U
1690 case 537: // DBAT0L
1691 case 538: // DBAT1U
1692 case 539: // DBAT1L
1693 case 540: // DBAT2U
1694 case 541: // DBAT2L
1695 case 542: // DBAT3U
1696 case 543: // DBAT3L
1697 case 952: // MMCR0
1698 case 953: // PMC1
1699 case 954: // PMC2
1700 case 955: // SIA
1701 case 956: // MMCR1
1702 case 957: // PMC3
1703 case 958: // PMC4
1704 case 959: // SDA
1705 r->nip += 4;
1706 goto rti;
1707 }
1708 break;
1709 }
1710
1711 case 29: case 107: case 152: case 153: // POWER instructions
1712 case 184: case 216: case 217: case 248:
1713 case 264: case 277: case 331: case 360:
1714 case 363: case 488: case 531: case 537:
1715 case 541: case 664: case 665: case 696:
1716 case 728: case 729: case 760: case 920:
1717 case 921: case 952:
1718 goto power_inst;
1719 }
1720 }
1721
1722 // In GUI mode, show error alert
1723 if (!PrefsFindBool("nogui")) {
1724 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->nip, r->gpr[24], r->gpr[1], opcode);
1725 ErrorAlert(str);
1726 QuitEmulator();
1727 return;
1728 }
1729 }
1730
1731 // For all other errors, jump into debugger (sort of...)
1732 if (!ready_for_signals) {
1733 printf("SIGILL\n");
1734 printf(" sigcontext %p, pt_regs %p\n", sc, r);
1735 printf(
1736 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
1737 " xer %08lx cr %08lx \n"
1738 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
1739 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
1740 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
1741 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
1742 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
1743 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
1744 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
1745 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
1746 r->nip, r->link, r->ctr, r->msr,
1747 r->xer, r->ccr,
1748 r->gpr[0], r->gpr[1], r->gpr[2], r->gpr[3],
1749 r->gpr[4], r->gpr[5], r->gpr[6], r->gpr[7],
1750 r->gpr[8], r->gpr[9], r->gpr[10], r->gpr[11],
1751 r->gpr[12], r->gpr[13], r->gpr[14], r->gpr[15],
1752 r->gpr[16], r->gpr[17], r->gpr[18], r->gpr[19],
1753 r->gpr[20], r->gpr[21], r->gpr[22], r->gpr[23],
1754 r->gpr[24], r->gpr[25], r->gpr[26], r->gpr[27],
1755 r->gpr[28], r->gpr[29], r->gpr[30], r->gpr[31]);
1756 exit(1);
1757 QuitEmulator();
1758 return;
1759 } else {
1760 // We crashed. Save registers, tell tick thread and loop forever
1761 sigsegv_regs = *(sigregs *)r;
1762 emul_thread_fatal = true;
1763 for (;;) ;
1764 }
1765 rti:;
1766 }
1767 #endif
1768
1769
1770 /*
1771 * Helpers to share 32-bit addressable data with MacOS
1772 */
1773
1774 bool SheepMem::Init(void)
1775 {
1776 if (vm_acquire_fixed((char *)base, size) < 0)
1777 return false;
1778
1779 zero_page = base + size;
1780
1781 int page_size = getpagesize();
1782 if (vm_acquire_fixed((char *)zero_page, page_size) < 0)
1783 return false;
1784 if (vm_protect((char *)zero_page, page_size, VM_PAGE_READ) < 0)
1785 return false;
1786
1787 top = base + size;
1788 return true;
1789 }
1790
1791 void SheepMem::Exit(void)
1792 {
1793 if (top) {
1794 // The zero page is next to SheepShaver globals
1795 vm_release((void *)base, size + getpagesize());
1796 }
1797 }
1798
1799
1800 /*
1801 * Display alert
1802 */
1803
1804 #ifdef ENABLE_GTK
1805 static void dl_destroyed(void)
1806 {
1807 gtk_main_quit();
1808 }
1809
1810 static void dl_quit(GtkWidget *dialog)
1811 {
1812 gtk_widget_destroy(dialog);
1813 }
1814
1815 void display_alert(int title_id, int prefix_id, int button_id, const char *text)
1816 {
1817 char str[256];
1818 sprintf(str, GetString(prefix_id), text);
1819
1820 GtkWidget *dialog = gtk_dialog_new();
1821 gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
1822 gtk_container_border_width(GTK_CONTAINER(dialog), 5);
1823 gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
1824 gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
1825
1826 GtkWidget *label = gtk_label_new(str);
1827 gtk_widget_show(label);
1828 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
1829
1830 GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
1831 gtk_widget_show(button);
1832 gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
1833 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
1834 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1835 gtk_widget_grab_default(button);
1836 gtk_widget_show(dialog);
1837
1838 gtk_main();
1839 }
1840 #endif
1841
1842
1843 /*
1844 * Display error alert
1845 */
1846
1847 void ErrorAlert(const char *text)
1848 {
1849 #ifdef ENABLE_GTK
1850 if (PrefsFindBool("nogui") || x_display == NULL) {
1851 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
1852 return;
1853 }
1854 VideoQuitFullScreen();
1855 display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
1856 #else
1857 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
1858 #endif
1859 }
1860
1861
1862 /*
1863 * Display warning alert
1864 */
1865
1866 void WarningAlert(const char *text)
1867 {
1868 #ifdef ENABLE_GTK
1869 if (PrefsFindBool("nogui") || x_display == NULL) {
1870 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1871 return;
1872 }
1873 display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
1874 #else
1875 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1876 #endif
1877 }
1878
1879
1880 /*
1881 * Display choice alert
1882 */
1883
1884 bool ChoiceAlert(const char *text, const char *pos, const char *neg)
1885 {
1886 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1887 return false; //!!
1888 }