ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/main_unix.cpp
Revision: 1.53
Committed: 2004-11-22T21:33:32Z (19 years, 6 months ago) by gbeauche
Branch: MAIN
Changes since 1.52: +137 -52 lines
Log Message:
New SheepShaver globals layout, move ZeroPage into the middle. Since it is
a read-only page, it can also be used to detect overlaps between Procedure
space and Data space.

Provide native Windows implementation of shared MacOS KernelData allocation.
This is moved under main() so that to avoid a weird linking error. This native
implementation is independent of Cygwin IPC (and possible background server)

File Contents

# Content
1 /*
2 * main_unix.cpp - Emulation core, Unix implementation
3 *
4 * SheepShaver (C) 1997-2004 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 * There is apparently another problem when processing signals. In
69 * fullscreen mode, we get quick updates of the mouse position. This
70 * causes an increased number of calls to TriggerInterrupt(). And,
71 * since IRQ_NEST is not fully handled atomically, nested calls to
72 * ppc_interrupt() may cause stack corruption to eventually crash the
73 * emulator.
74 *
75 * FIXME:
76 * The current solution is to allocate another signal stack when
77 * processing ppc_interrupt(). However, it may be better to detect
78 * the INTFLAG_ADB case and handle it specifically with some extra mutex?
79 *
80 * TODO:
81 * check if SIGSEGV handler works for all registers (including FP!)
82 */
83
84 #include <unistd.h>
85 #include <fcntl.h>
86 #include <time.h>
87 #include <errno.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <string.h>
91 #include <pthread.h>
92 #include <sys/mman.h>
93 #include <sys/ipc.h>
94 #include <sys/shm.h>
95 #include <signal.h>
96
97 #include "sysdeps.h"
98 #include "main.h"
99 #include "version.h"
100 #include "prefs.h"
101 #include "prefs_editor.h"
102 #include "cpu_emulation.h"
103 #include "emul_op.h"
104 #include "xlowmem.h"
105 #include "xpram.h"
106 #include "timer.h"
107 #include "adb.h"
108 #include "sony.h"
109 #include "disk.h"
110 #include "cdrom.h"
111 #include "scsi.h"
112 #include "video.h"
113 #include "audio.h"
114 #include "ether.h"
115 #include "serial.h"
116 #include "clip.h"
117 #include "extfs.h"
118 #include "sys.h"
119 #include "macos_util.h"
120 #include "rom_patches.h"
121 #include "user_strings.h"
122 #include "vm_alloc.h"
123 #include "sigsegv.h"
124 #include "thunks.h"
125
126 #define DEBUG 0
127 #include "debug.h"
128
129
130 #ifdef HAVE_DIRENT_H
131 #include <dirent.h>
132 #endif
133
134 #ifdef USE_SDL
135 #include <SDL.h>
136 #endif
137
138 #ifndef USE_SDL_VIDEO
139 #include <X11/Xlib.h>
140 #endif
141
142 #ifdef ENABLE_GTK
143 #include <gtk/gtk.h>
144 #endif
145
146 #ifdef ENABLE_XF86_DGA
147 #include <X11/Xlib.h>
148 #include <X11/Xutil.h>
149 #include <X11/extensions/xf86dga.h>
150 #endif
151
152 #ifdef ENABLE_MON
153 #include "mon.h"
154 #endif
155
156
157 // Enable emulation of unaligned lmw/stmw?
158 #define EMULATE_UNALIGNED_LOADSTORE_MULTIPLE 1
159
160 // Enable Execute68k() safety checks?
161 #define SAFE_EXEC_68K 0
162
163 // Interrupts in EMUL_OP mode?
164 #define INTERRUPTS_IN_EMUL_OP_MODE 1
165
166 // Interrupts in native mode?
167 #define INTERRUPTS_IN_NATIVE_MODE 1
168
169 // Number of alternate stacks for signal handlers?
170 #define SIG_STACK_COUNT 4
171
172
173 // Constants
174 const char ROM_FILE_NAME[] = "ROM";
175 const char ROM_FILE_NAME2[] = "Mac OS ROM";
176
177 #if REAL_ADDRESSING
178 const uintptr RAM_BASE = 0x20000000; // Base address of RAM
179 #else
180 // FIXME: needs to be >= 0x04000000
181 const uintptr RAM_BASE = 0x10000000; // Base address of RAM
182 #endif
183 const uint32 SIG_STACK_SIZE = 0x10000; // Size of signal stack
184
185
186 #if !EMULATED_PPC
187 struct sigregs {
188 uint32 nip;
189 uint32 link;
190 uint32 ctr;
191 uint32 msr;
192 uint32 xer;
193 uint32 ccr;
194 uint32 gpr[32];
195 };
196
197 #if defined(__linux__)
198 #include <sys/ucontext.h>
199 #define MACHINE_REGISTERS(scp) ((machine_regs *)(((ucontext_t *)scp)->uc_mcontext.regs))
200
201 struct machine_regs : public pt_regs
202 {
203 u_long & cr() { return pt_regs::ccr; }
204 uint32 cr() const { return pt_regs::ccr; }
205 uint32 lr() const { return pt_regs::link; }
206 uint32 ctr() const { return pt_regs::ctr; }
207 uint32 xer() const { return pt_regs::xer; }
208 uint32 msr() const { return pt_regs::msr; }
209 uint32 dar() const { return pt_regs::dar; }
210 u_long & pc() { return pt_regs::nip; }
211 uint32 pc() const { return pt_regs::nip; }
212 u_long & gpr(int i) { return pt_regs::gpr[i]; }
213 uint32 gpr(int i) const { return pt_regs::gpr[i]; }
214 };
215 #endif
216
217 #if defined(__APPLE__) && defined(__MACH__)
218 #include <sys/signal.h>
219 extern "C" int sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss);
220
221 #include <sys/ucontext.h>
222 #define MACHINE_REGISTERS(scp) ((machine_regs *)(((ucontext_t *)scp)->uc_mcontext))
223
224 struct machine_regs : public mcontext
225 {
226 uint32 & cr() { return ss.cr; }
227 uint32 cr() const { return ss.cr; }
228 uint32 lr() const { return ss.lr; }
229 uint32 ctr() const { return ss.ctr; }
230 uint32 xer() const { return ss.xer; }
231 uint32 msr() const { return ss.srr1; }
232 uint32 dar() const { return es.dar; }
233 uint32 & pc() { return ss.srr0; }
234 uint32 pc() const { return ss.srr0; }
235 uint32 & gpr(int i) { return (&ss.r0)[i]; }
236 uint32 gpr(int i) const { return (&ss.r0)[i]; }
237 };
238 #endif
239
240 static void build_sigregs(sigregs *srp, machine_regs *mrp)
241 {
242 srp->nip = mrp->pc();
243 srp->link = mrp->lr();
244 srp->ctr = mrp->ctr();
245 srp->msr = mrp->msr();
246 srp->xer = mrp->xer();
247 srp->ccr = mrp->cr();
248 for (int i = 0; i < 32; i++)
249 srp->gpr[i] = mrp->gpr(i);
250 }
251
252 static struct sigaltstack sig_stacks[SIG_STACK_COUNT]; // Stacks for signal handlers
253 static int sig_stack_id = 0; // Stack slot currently used
254
255 static inline void sig_stack_acquire(void)
256 {
257 if (++sig_stack_id == SIG_STACK_COUNT) {
258 printf("FATAL: signal stack overflow\n");
259 return;
260 }
261 sigaltstack(&sig_stacks[sig_stack_id], NULL);
262 }
263
264 static inline void sig_stack_release(void)
265 {
266 if (--sig_stack_id < 0) {
267 printf("FATAL: signal stack underflow\n");
268 return;
269 }
270 sigaltstack(&sig_stacks[sig_stack_id], NULL);
271 }
272 #endif
273
274
275 // Global variables (exported)
276 #if !EMULATED_PPC
277 void *TOC; // Small data pointer (r13)
278 #endif
279 uint32 RAMBase; // Base address of Mac RAM
280 uint32 RAMSize; // Size of Mac RAM
281 uint32 KernelDataAddr; // Address of Kernel Data
282 uint32 BootGlobsAddr; // Address of BootGlobs structure at top of Mac RAM
283 uint32 DRCacheAddr; // Address of DR Cache
284 uint32 PVR; // Theoretical PVR
285 int64 CPUClockSpeed; // Processor clock speed (Hz)
286 int64 BusClockSpeed; // Bus clock speed (Hz)
287 int64 TimebaseSpeed; // Timebase clock speed (Hz)
288 uint8 *RAMBaseHost; // Base address of Mac RAM (host address space)
289 uint8 *ROMBaseHost; // Base address of Mac ROM (host address space)
290
291
292 // Global variables
293 #ifndef USE_SDL_VIDEO
294 char *x_display_name = NULL; // X11 display name
295 Display *x_display = NULL; // X11 display handle
296 #ifdef X11_LOCK_TYPE
297 X11_LOCK_TYPE x_display_lock = X11_LOCK_INIT; // X11 display lock
298 #endif
299 #endif
300
301 static int zero_fd = 0; // FD of /dev/zero
302 static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
303 static int kernel_area = -1; // SHM ID of Kernel Data area
304 static bool rom_area_mapped = false; // Flag: Mac ROM mmap()ped
305 static bool ram_area_mapped = false; // Flag: Mac RAM mmap()ped
306 static bool dr_cache_area_mapped = false; // Flag: Mac DR Cache mmap()ped
307 static bool dr_emulator_area_mapped = false;// Flag: Mac DR Emulator mmap()ped
308 static KernelData *kernel_data; // Pointer to Kernel Data
309 static EmulatorData *emulator_data;
310
311 static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes
312
313 static bool nvram_thread_active = false; // Flag: NVRAM watchdog installed
314 static volatile bool nvram_thread_cancel; // Flag: Cancel NVRAM thread
315 static pthread_t nvram_thread; // NVRAM watchdog
316 static bool tick_thread_active = false; // Flag: MacOS thread installed
317 static volatile bool tick_thread_cancel; // Flag: Cancel 60Hz thread
318 static pthread_t tick_thread; // 60Hz thread
319 static pthread_t emul_thread; // MacOS thread
320
321 static bool ready_for_signals = false; // Handler installed, signals can be sent
322 static int64 num_segv = 0; // Number of handled SEGV signals
323
324 static struct sigaction sigusr2_action; // Interrupt signal (of emulator thread)
325 #if EMULATED_PPC
326 static uintptr sig_stack = 0; // Stack for PowerPC interrupt routine
327 #else
328 static struct sigaction sigsegv_action; // Data access exception signal (of emulator thread)
329 static struct sigaction sigill_action; // Illegal instruction signal (of emulator thread)
330 static bool emul_thread_fatal = false; // Flag: MacOS thread crashed, tick thread shall dump debug output
331 static sigregs sigsegv_regs; // Register dump when crashed
332 static const char *crash_reason = NULL; // Reason of the crash (SIGSEGV, SIGBUS, SIGILL)
333 #endif
334
335 uint32 SheepMem::page_size; // Size of a native page
336 uintptr SheepMem::zero_page = 0; // Address of ro page filled in with zeros
337 uintptr SheepMem::base = 0x60000000; // Address of SheepShaver data
338 uintptr SheepMem::proc; // Bottom address of SheepShave procedures
339 uintptr SheepMem::data; // Top of SheepShaver data (stack like storage)
340
341
342 // Prototypes
343 static bool kernel_data_init(void);
344 static void kernel_data_exit(void);
345 static void Quit(void);
346 static void *emul_func(void *arg);
347 static void *nvram_func(void *arg);
348 static void *tick_func(void *arg);
349 #if EMULATED_PPC
350 extern void emul_ppc(uint32 start);
351 extern void init_emul_ppc(void);
352 extern void exit_emul_ppc(void);
353 sigsegv_return_t sigsegv_handler(sigsegv_address_t, sigsegv_address_t);
354 #else
355 static void sigusr2_handler(int sig, siginfo_t *sip, void *scp);
356 static void sigsegv_handler(int sig, siginfo_t *sip, void *scp);
357 static void sigill_handler(int sig, siginfo_t *sip, void *scp);
358 #endif
359
360
361 // From asm_linux.S
362 #if !EMULATED_PPC
363 extern "C" void *get_toc(void);
364 extern "C" void *get_sp(void);
365 extern "C" void flush_icache_range(void *start, void *end);
366 extern "C" void jump_to_rom(uint32 entry, uint32 context);
367 extern "C" void quit_emulator(void);
368 extern "C" void execute_68k(uint32 pc, M68kRegisters *r);
369 extern "C" void ppc_interrupt(uint32 entry, uint32 kernel_data);
370 extern "C" int atomic_add(int *var, int v);
371 extern "C" int atomic_and(int *var, int v);
372 extern "C" int atomic_or(int *var, int v);
373 extern void paranoia_check(void);
374 #endif
375
376
377 #if EMULATED_PPC
378 /*
379 * Return signal stack base
380 */
381
382 uintptr SignalStackBase(void)
383 {
384 return sig_stack + SIG_STACK_SIZE;
385 }
386
387
388 /*
389 * Atomic operations
390 */
391
392 #if HAVE_SPINLOCKS
393 static spinlock_t atomic_ops_lock = SPIN_LOCK_UNLOCKED;
394 #else
395 #define spin_lock(LOCK)
396 #define spin_unlock(LOCK)
397 #endif
398
399 int atomic_add(int *var, int v)
400 {
401 spin_lock(&atomic_ops_lock);
402 int ret = *var;
403 *var += v;
404 spin_unlock(&atomic_ops_lock);
405 return ret;
406 }
407
408 int atomic_and(int *var, int v)
409 {
410 spin_lock(&atomic_ops_lock);
411 int ret = *var;
412 *var &= v;
413 spin_unlock(&atomic_ops_lock);
414 return ret;
415 }
416
417 int atomic_or(int *var, int v)
418 {
419 spin_lock(&atomic_ops_lock);
420 int ret = *var;
421 *var |= v;
422 spin_unlock(&atomic_ops_lock);
423 return ret;
424 }
425 #endif
426
427
428 /*
429 * Memory management helpers
430 */
431
432 static inline int vm_mac_acquire(uint32 addr, uint32 size)
433 {
434 return vm_acquire_fixed(Mac2HostAddr(addr), size);
435 }
436
437 static inline int vm_mac_release(uint32 addr, uint32 size)
438 {
439 return vm_release(Mac2HostAddr(addr), size);
440 }
441
442
443 /*
444 * Main program
445 */
446
447 static void usage(const char *prg_name)
448 {
449 printf("Usage: %s [OPTION...]\n", prg_name);
450 printf("\nUnix options:\n");
451 printf(" --display STRING\n X display to use\n");
452 PrefsPrintUsage();
453 exit(0);
454 }
455
456 int main(int argc, char **argv)
457 {
458 char str[256];
459 int16 i16;
460 int rom_fd;
461 FILE *proc_file;
462 const char *rom_path;
463 uint32 rom_size, actual;
464 uint8 *rom_tmp;
465 time_t now, expire;
466
467 // Initialize variables
468 RAMBase = 0;
469 tzset();
470
471 // Print some info
472 printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
473 printf(" %s\n", GetString(STR_ABOUT_TEXT2));
474
475 #if !EMULATED_PPC
476 // Get TOC pointer
477 TOC = get_toc();
478 #endif
479
480 #ifdef ENABLE_GTK
481 // Init GTK
482 gtk_set_locale();
483 gtk_init(&argc, &argv);
484 #endif
485
486 // Read preferences
487 PrefsInit(argc, argv);
488
489 // Parse command line arguments
490 for (int i=1; i<argc; i++) {
491 if (strcmp(argv[i], "--help") == 0) {
492 usage(argv[0]);
493 #ifndef USE_SDL_VIDEO
494 } else if (strcmp(argv[i], "--display") == 0) {
495 i++;
496 if (i < argc)
497 x_display_name = strdup(argv[i]);
498 #endif
499 } else if (argv[i][0] == '-') {
500 fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
501 usage(argv[0]);
502 }
503 }
504
505 #ifdef USE_SDL
506 // Initialize SDL system
507 int sdl_flags = 0;
508 #ifdef USE_SDL_VIDEO
509 sdl_flags |= SDL_INIT_VIDEO;
510 #endif
511 #ifdef USE_SDL_AUDIO
512 sdl_flags |= SDL_INIT_AUDIO;
513 #endif
514 assert(sdl_flags != 0);
515 if (SDL_Init(sdl_flags) == -1) {
516 char str[256];
517 sprintf(str, "Could not initialize SDL: %s.\n", SDL_GetError());
518 ErrorAlert(str);
519 goto quit;
520 }
521 atexit(SDL_Quit);
522 #endif
523
524 #ifndef USE_SDL_VIDEO
525 // Open display
526 x_display = XOpenDisplay(x_display_name);
527 if (x_display == NULL) {
528 char str[256];
529 sprintf(str, GetString(STR_NO_XSERVER_ERR), XDisplayName(x_display_name));
530 ErrorAlert(str);
531 goto quit;
532 }
533
534 #if defined(ENABLE_XF86_DGA) && !defined(ENABLE_MON)
535 // Fork out, so we can return from fullscreen mode when things get ugly
536 XF86DGAForkApp(DefaultScreen(x_display));
537 #endif
538 #endif
539
540 #ifdef ENABLE_MON
541 // Initialize mon
542 mon_init();
543 #endif
544
545 #if !EMULATED_PPC
546 // Create and install stacks for signal handlers
547 for (int i = 0; i < SIG_STACK_COUNT; i++) {
548 void *sig_stack = malloc(SIG_STACK_SIZE);
549 D(bug("Signal stack %d at %p\n", i, sig_stack));
550 if (sig_stack == NULL) {
551 ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR));
552 goto quit;
553 }
554 sig_stacks[i].ss_sp = sig_stack;
555 sig_stacks[i].ss_flags = 0;
556 sig_stacks[i].ss_size = SIG_STACK_SIZE;
557 }
558 sig_stack_id = 0;
559 if (sigaltstack(&sig_stacks[0], NULL) < 0) {
560 sprintf(str, GetString(STR_SIGALTSTACK_ERR), strerror(errno));
561 ErrorAlert(str);
562 goto quit;
563 }
564 #endif
565
566 #if !EMULATED_PPC
567 // Install SIGSEGV and SIGBUS handlers
568 sigemptyset(&sigsegv_action.sa_mask); // Block interrupts during SEGV handling
569 sigaddset(&sigsegv_action.sa_mask, SIGUSR2);
570 sigsegv_action.sa_sigaction = sigsegv_handler;
571 sigsegv_action.sa_flags = SA_ONSTACK | SA_SIGINFO;
572 #ifdef HAVE_SIGNAL_SA_RESTORER
573 sigsegv_action.sa_restorer = NULL;
574 #endif
575 if (sigaction(SIGSEGV, &sigsegv_action, NULL) < 0) {
576 sprintf(str, GetString(STR_SIGSEGV_INSTALL_ERR), strerror(errno));
577 ErrorAlert(str);
578 goto quit;
579 }
580 if (sigaction(SIGBUS, &sigsegv_action, NULL) < 0) {
581 sprintf(str, GetString(STR_SIGSEGV_INSTALL_ERR), strerror(errno));
582 ErrorAlert(str);
583 goto quit;
584 }
585 #else
586 // Install SIGSEGV handler for CPU emulator
587 if (!sigsegv_install_handler(sigsegv_handler)) {
588 sprintf(str, GetString(STR_SIGSEGV_INSTALL_ERR), strerror(errno));
589 ErrorAlert(str);
590 goto quit;
591 }
592 #endif
593
594 // Initialize VM system
595 vm_init();
596
597 // Get system info
598 PVR = 0x00040000; // Default: 604
599 CPUClockSpeed = 100000000; // Default: 100MHz
600 BusClockSpeed = 100000000; // Default: 100MHz
601 TimebaseSpeed = 25000000; // Default: 25MHz
602 #if EMULATED_PPC
603 PVR = 0x000c0000; // Default: 7400 (with AltiVec)
604 #elif defined(__APPLE__) && defined(__MACH__)
605 proc_file = popen("ioreg -c IOPlatformDevice", "r");
606 if (proc_file) {
607 char line[256];
608 bool powerpc_node = false;
609 while (fgets(line, sizeof(line) - 1, proc_file)) {
610 // Read line
611 int len = strlen(line);
612 if (len == 0)
613 continue;
614 line[len - 1] = 0;
615
616 // Parse line
617 if (strstr(line, "o PowerPC,"))
618 powerpc_node = true;
619 else if (powerpc_node) {
620 uint32 value;
621 char head[256];
622 if (sscanf(line, "%[ |]\"cpu-version\" = <%x>", head, &value) == 2)
623 PVR = value;
624 else if (sscanf(line, "%[ |]\"clock-frequency\" = <%x>", head, &value) == 2)
625 CPUClockSpeed = value;
626 else if (sscanf(line, "%[ |]\"bus-frequency\" = <%x>", head, &value) == 2)
627 BusClockSpeed = value;
628 else if (sscanf(line, "%[ |]\"timebase-frequency\" = <%x>", head, &value) == 2)
629 TimebaseSpeed = value;
630 else if (strchr(line, '}'))
631 powerpc_node = false;
632 }
633 }
634 fclose(proc_file);
635 } else {
636 sprintf(str, GetString(STR_PROC_CPUINFO_WARN), strerror(errno));
637 WarningAlert(str);
638 }
639 #else
640 proc_file = fopen("/proc/cpuinfo", "r");
641 if (proc_file) {
642 // CPU specs from Linux kernel
643 // TODO: make it more generic with features (e.g. AltiVec) and
644 // cache information and friends for NameRegistry
645 static const struct {
646 uint32 pvr_mask;
647 uint32 pvr_value;
648 const char *cpu_name;
649 }
650 cpu_specs[] = {
651 { 0xffff0000, 0x00010000, "601" },
652 { 0xffff0000, 0x00030000, "603" },
653 { 0xffff0000, 0x00060000, "603e" },
654 { 0xffff0000, 0x00070000, "603ev" },
655 { 0xffff0000, 0x00040000, "604" },
656 { 0xfffff000, 0x00090000, "604e" },
657 { 0xffff0000, 0x00090000, "604r" },
658 { 0xffff0000, 0x000a0000, "604ev" },
659 { 0xffffffff, 0x00084202, "740/750" },
660 { 0xfffff000, 0x00083000, "745/755" },
661 { 0xfffffff0, 0x00080100, "750CX" },
662 { 0xfffffff0, 0x00082200, "750CX" },
663 { 0xfffffff0, 0x00082210, "750CXe" },
664 { 0xffffff00, 0x70000100, "750FX" },
665 { 0xffffffff, 0x70000200, "750FX" },
666 { 0xffff0000, 0x70000000, "750FX" },
667 { 0xffff0000, 0x70020000, "750GX" },
668 { 0xffff0000, 0x00080000, "740/750" },
669 { 0xffffffff, 0x000c1101, "7400 (1.1)" },
670 { 0xffff0000, 0x000c0000, "7400" },
671 { 0xffff0000, 0x800c0000, "7410" },
672 { 0xffffffff, 0x80000200, "7450" },
673 { 0xffffffff, 0x80000201, "7450" },
674 { 0xffff0000, 0x80000000, "7450" },
675 { 0xffffff00, 0x80010100, "7455" },
676 { 0xffffffff, 0x80010200, "7455" },
677 { 0xffff0000, 0x80010000, "7455" },
678 { 0xffff0000, 0x80020000, "7457" },
679 { 0xffff0000, 0x80030000, "7447A" },
680 { 0x7fff0000, 0x00810000, "82xx" },
681 { 0x7fff0000, 0x00820000, "8280" },
682 { 0xffff0000, 0x00400000, "Power3 (630)" },
683 { 0xffff0000, 0x00410000, "Power3 (630+)" },
684 { 0xffff0000, 0x00360000, "I-star" },
685 { 0xffff0000, 0x00370000, "S-star" },
686 { 0xffff0000, 0x00350000, "Power4" },
687 { 0xffff0000, 0x00390000, "PPC970" },
688 { 0, 0, 0 }
689 };
690
691 char line[256];
692 while(fgets(line, 255, proc_file)) {
693 // Read line
694 int len = strlen(line);
695 if (len == 0)
696 continue;
697 line[len-1] = 0;
698
699 // Parse line
700 int i;
701 char value[256];
702 if (sscanf(line, "cpu : %[0-9A-Za-a]", value) == 1) {
703 // Search by name
704 const char *cpu_name = NULL;
705 for (int i = 0; cpu_specs[i].pvr_mask != 0; i++) {
706 if (strcmp(cpu_specs[i].cpu_name, value) == 0) {
707 cpu_name = cpu_specs[i].cpu_name;
708 PVR = cpu_specs[i].pvr_value;
709 break;
710 }
711 }
712 if (cpu_name == NULL)
713 printf("WARNING: Unknown CPU type '%s', assuming 604\n", value);
714 else
715 printf("Found a PowerPC %s processor\n", cpu_name);
716 }
717 if (sscanf(line, "clock : %dMHz", &i) == 1)
718 CPUClockSpeed = BusClockSpeed = i * 1000000;
719 }
720 fclose(proc_file);
721 } else {
722 sprintf(str, GetString(STR_PROC_CPUINFO_WARN), strerror(errno));
723 WarningAlert(str);
724 }
725
726 // Get actual bus frequency
727 proc_file = fopen("/proc/device-tree/clock-frequency", "r");
728 if (proc_file) {
729 union { uint8 b[4]; uint32 l; } value;
730 if (fread(value.b, sizeof(value), 1, proc_file) == 1)
731 BusClockSpeed = value.l;
732 fclose(proc_file);
733 }
734
735 // Get actual timebase frequency
736 TimebaseSpeed = BusClockSpeed / 4;
737 DIR *cpus_dir;
738 if ((cpus_dir = opendir("/proc/device-tree/cpus")) != NULL) {
739 struct dirent *cpu_entry;
740 while ((cpu_entry = readdir(cpus_dir)) != NULL) {
741 if (strstr(cpu_entry->d_name, "PowerPC,") == cpu_entry->d_name) {
742 char timebase_freq_node[256];
743 sprintf(timebase_freq_node, "/proc/device-tree/cpus/%s/timebase-frequency", cpu_entry->d_name);
744 proc_file = fopen(timebase_freq_node, "r");
745 if (proc_file) {
746 union { uint8 b[4]; uint32 l; } value;
747 if (fread(value.b, sizeof(value), 1, proc_file) == 1)
748 TimebaseSpeed = value.l;
749 fclose(proc_file);
750 }
751 }
752 }
753 closedir(cpus_dir);
754 }
755 #endif
756 // Remap any newer G4/G5 processor to plain G4 for compatibility
757 switch (PVR >> 16) {
758 case 0x8000: // 7450
759 case 0x8001: // 7455
760 case 0x8002: // 7457
761 case 0x0039: // 970
762 PVR = 0x000c0000; // 7400
763 break;
764 }
765 D(bug("PVR: %08x (assumed)\n", PVR));
766
767 // Init system routines
768 SysInit();
769
770 // Show preferences editor
771 if (!PrefsFindBool("nogui"))
772 if (!PrefsEditor())
773 goto quit;
774
775 #if !EMULATED_PPC
776 // Check some things
777 paranoia_check();
778 #endif
779
780 // Open /dev/zero
781 zero_fd = open("/dev/zero", O_RDWR);
782 if (zero_fd < 0) {
783 sprintf(str, GetString(STR_NO_DEV_ZERO_ERR), strerror(errno));
784 ErrorAlert(str);
785 goto quit;
786 }
787
788 #ifndef PAGEZERO_HACK
789 // Create Low Memory area (0x0000..0x3000)
790 if (vm_mac_acquire(0, 0x3000) < 0) {
791 sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno));
792 ErrorAlert(str);
793 goto quit;
794 }
795 lm_area_mapped = true;
796 #endif
797
798 // Create areas for Kernel Data
799 if (!kernel_data_init())
800 goto quit;
801 kernel_data = (KernelData *)Mac2HostAddr(KERNEL_DATA_BASE);
802 emulator_data = &kernel_data->ed;
803 KernelDataAddr = KERNEL_DATA_BASE;
804 D(bug("Kernel Data at %p (%08x)\n", kernel_data, KERNEL_DATA_BASE));
805 D(bug("Emulator Data at %p (%08x)\n", emulator_data, KERNEL_DATA_BASE + offsetof(KernelData, ed)));
806
807 // Create area for DR Cache
808 if (vm_mac_acquire(DR_EMULATOR_BASE, DR_EMULATOR_SIZE) < 0) {
809 sprintf(str, GetString(STR_DR_EMULATOR_MMAP_ERR), strerror(errno));
810 ErrorAlert(str);
811 goto quit;
812 }
813 dr_emulator_area_mapped = true;
814 if (vm_mac_acquire(DR_CACHE_BASE, DR_CACHE_SIZE) < 0) {
815 sprintf(str, GetString(STR_DR_CACHE_MMAP_ERR), strerror(errno));
816 ErrorAlert(str);
817 goto quit;
818 }
819 dr_cache_area_mapped = true;
820 #if !EMULATED_PPC
821 if (vm_protect((char *)DR_CACHE_BASE, DR_CACHE_SIZE, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
822 sprintf(str, GetString(STR_DR_CACHE_MMAP_ERR), strerror(errno));
823 ErrorAlert(str);
824 goto quit;
825 }
826 #endif
827 DRCacheAddr = DR_CACHE_BASE;
828 D(bug("DR Cache at %p\n", DRCacheAddr));
829
830 // Create area for SheepShaver data
831 if (!SheepMem::Init()) {
832 sprintf(str, GetString(STR_SHEEP_MEM_MMAP_ERR), strerror(errno));
833 ErrorAlert(str);
834 goto quit;
835 }
836
837 // Create area for Mac ROM
838 if (vm_mac_acquire(ROM_BASE, ROM_AREA_SIZE) < 0) {
839 sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno));
840 ErrorAlert(str);
841 goto quit;
842 }
843 ROMBaseHost = Mac2HostAddr(ROM_BASE);
844 #if !EMULATED_PPC
845 if (vm_protect(ROMBaseHost, ROM_AREA_SIZE, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
846 sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno));
847 ErrorAlert(str);
848 goto quit;
849 }
850 #endif
851 rom_area_mapped = true;
852 D(bug("ROM area at %p (%08x)\n", ROMBaseHost, ROM_BASE));
853
854 // Create area for Mac RAM
855 RAMSize = PrefsFindInt32("ramsize");
856 if (RAMSize < 8*1024*1024) {
857 WarningAlert(GetString(STR_SMALL_RAM_WARN));
858 RAMSize = 8*1024*1024;
859 }
860
861 if (vm_mac_acquire(RAM_BASE, RAMSize) < 0) {
862 sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno));
863 ErrorAlert(str);
864 goto quit;
865 }
866 RAMBaseHost = Mac2HostAddr(RAM_BASE);
867 #if !EMULATED_PPC
868 if (vm_protect(RAMBaseHost, RAMSize, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
869 sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno));
870 ErrorAlert(str);
871 goto quit;
872 }
873 #endif
874 RAMBase = RAM_BASE;
875 ram_area_mapped = true;
876 D(bug("RAM area at %p (%08x)\n", RAMBaseHost, RAMBase));
877
878 if (RAMBase > ROM_BASE) {
879 ErrorAlert(GetString(STR_RAM_HIGHER_THAN_ROM_ERR));
880 goto quit;
881 }
882
883 // Load Mac ROM
884 rom_path = PrefsFindString("rom");
885 rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY);
886 if (rom_fd < 0) {
887 rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME2, O_RDONLY);
888 if (rom_fd < 0) {
889 ErrorAlert(GetString(STR_NO_ROM_FILE_ERR));
890 goto quit;
891 }
892 }
893 printf(GetString(STR_READING_ROM_FILE));
894 rom_size = lseek(rom_fd, 0, SEEK_END);
895 lseek(rom_fd, 0, SEEK_SET);
896 rom_tmp = new uint8[ROM_SIZE];
897 actual = read(rom_fd, (void *)rom_tmp, ROM_SIZE);
898 close(rom_fd);
899
900 // Decode Mac ROM
901 if (!DecodeROM(rom_tmp, actual)) {
902 if (rom_size != 4*1024*1024) {
903 ErrorAlert(GetString(STR_ROM_SIZE_ERR));
904 goto quit;
905 } else {
906 ErrorAlert(GetString(STR_ROM_FILE_READ_ERR));
907 goto quit;
908 }
909 }
910 delete[] rom_tmp;
911
912 // Load NVRAM
913 XPRAMInit();
914
915 // Load XPRAM default values if signature not found
916 if (XPRAM[0x130c] != 0x4e || XPRAM[0x130d] != 0x75
917 || XPRAM[0x130e] != 0x4d || XPRAM[0x130f] != 0x63) {
918 D(bug("Loading XPRAM default values\n"));
919 memset(XPRAM + 0x1300, 0, 0x100);
920 XPRAM[0x130c] = 0x4e; // "NuMc" signature
921 XPRAM[0x130d] = 0x75;
922 XPRAM[0x130e] = 0x4d;
923 XPRAM[0x130f] = 0x63;
924 XPRAM[0x1301] = 0x80; // InternalWaitFlags = DynWait (don't wait for SCSI devices upon bootup)
925 XPRAM[0x1310] = 0xa8; // Standard PRAM values
926 XPRAM[0x1311] = 0x00;
927 XPRAM[0x1312] = 0x00;
928 XPRAM[0x1313] = 0x22;
929 XPRAM[0x1314] = 0xcc;
930 XPRAM[0x1315] = 0x0a;
931 XPRAM[0x1316] = 0xcc;
932 XPRAM[0x1317] = 0x0a;
933 XPRAM[0x131c] = 0x00;
934 XPRAM[0x131d] = 0x02;
935 XPRAM[0x131e] = 0x63;
936 XPRAM[0x131f] = 0x00;
937 XPRAM[0x1308] = 0x13;
938 XPRAM[0x1309] = 0x88;
939 XPRAM[0x130a] = 0x00;
940 XPRAM[0x130b] = 0xcc;
941 XPRAM[0x1376] = 0x00; // OSDefault = MacOS
942 XPRAM[0x1377] = 0x01;
943 }
944
945 // Set boot volume
946 i16 = PrefsFindInt32("bootdrive");
947 XPRAM[0x1378] = i16 >> 8;
948 XPRAM[0x1379] = i16 & 0xff;
949 i16 = PrefsFindInt32("bootdriver");
950 XPRAM[0x137a] = i16 >> 8;
951 XPRAM[0x137b] = i16 & 0xff;
952
953 // Create BootGlobs at top of Mac memory
954 memset(RAMBaseHost + RAMSize - 4096, 0, 4096);
955 BootGlobsAddr = RAMBase + RAMSize - 0x1c;
956 WriteMacInt32(BootGlobsAddr - 5 * 4, RAMBase + RAMSize); // MemTop
957 WriteMacInt32(BootGlobsAddr + 0 * 4, RAMBase); // First RAM bank
958 WriteMacInt32(BootGlobsAddr + 1 * 4, RAMSize);
959 WriteMacInt32(BootGlobsAddr + 2 * 4, (uint32)-1); // End of bank table
960
961 // Init thunks
962 if (!ThunksInit())
963 goto quit;
964
965 // Init drivers
966 SonyInit();
967 DiskInit();
968 CDROMInit();
969 SCSIInit();
970
971 // Init external file system
972 ExtFSInit();
973
974 // Init ADB
975 ADBInit();
976
977 // Init audio
978 AudioInit();
979
980 // Init network
981 EtherInit();
982
983 // Init serial ports
984 SerialInit();
985
986 // Init Time Manager
987 TimerInit();
988
989 // Init clipboard
990 ClipInit();
991
992 // Init video
993 if (!VideoInit())
994 goto quit;
995
996 // Install ROM patches
997 if (!PatchROM()) {
998 ErrorAlert(GetString(STR_UNSUPPORTED_ROM_TYPE_ERR));
999 goto quit;
1000 }
1001
1002 // Clear caches (as we loaded and patched code) and write protect ROM
1003 #if !EMULATED_PPC
1004 MakeExecutable(0, ROM_BASE, ROM_AREA_SIZE);
1005 #endif
1006 vm_protect(ROMBaseHost, ROM_AREA_SIZE, VM_PAGE_READ | VM_PAGE_EXECUTE);
1007
1008 // Initialize Kernel Data
1009 memset(kernel_data, 0, sizeof(KernelData));
1010 if (ROMType == ROMTYPE_NEWWORLD) {
1011 uint32 of_dev_tree = SheepMem::Reserve(4 * sizeof(uint32));
1012 Mac_memset(of_dev_tree, 0, 4 * sizeof(uint32));
1013 uint32 vector_lookup_tbl = SheepMem::Reserve(128);
1014 uint32 vector_mask_tbl = SheepMem::Reserve(64);
1015 memset((uint8 *)kernel_data + 0xb80, 0x3d, 0x80);
1016 Mac_memset(vector_lookup_tbl, 0, 128);
1017 Mac_memset(vector_mask_tbl, 0, 64);
1018 kernel_data->v[0xb80 >> 2] = htonl(ROM_BASE);
1019 kernel_data->v[0xb84 >> 2] = htonl(of_dev_tree); // OF device tree base
1020 kernel_data->v[0xb90 >> 2] = htonl(vector_lookup_tbl);
1021 kernel_data->v[0xb94 >> 2] = htonl(vector_mask_tbl);
1022 kernel_data->v[0xb98 >> 2] = htonl(ROM_BASE); // OpenPIC base
1023 kernel_data->v[0xbb0 >> 2] = htonl(0); // ADB base
1024 kernel_data->v[0xc20 >> 2] = htonl(RAMSize);
1025 kernel_data->v[0xc24 >> 2] = htonl(RAMSize);
1026 kernel_data->v[0xc30 >> 2] = htonl(RAMSize);
1027 kernel_data->v[0xc34 >> 2] = htonl(RAMSize);
1028 kernel_data->v[0xc38 >> 2] = htonl(0x00010020);
1029 kernel_data->v[0xc3c >> 2] = htonl(0x00200001);
1030 kernel_data->v[0xc40 >> 2] = htonl(0x00010000);
1031 kernel_data->v[0xc50 >> 2] = htonl(RAMBase);
1032 kernel_data->v[0xc54 >> 2] = htonl(RAMSize);
1033 kernel_data->v[0xf60 >> 2] = htonl(PVR);
1034 kernel_data->v[0xf64 >> 2] = htonl(CPUClockSpeed); // clock-frequency
1035 kernel_data->v[0xf68 >> 2] = htonl(BusClockSpeed); // bus-frequency
1036 kernel_data->v[0xf6c >> 2] = htonl(TimebaseSpeed); // timebase-frequency
1037 } else {
1038 kernel_data->v[0xc80 >> 2] = htonl(RAMSize);
1039 kernel_data->v[0xc84 >> 2] = htonl(RAMSize);
1040 kernel_data->v[0xc90 >> 2] = htonl(RAMSize);
1041 kernel_data->v[0xc94 >> 2] = htonl(RAMSize);
1042 kernel_data->v[0xc98 >> 2] = htonl(0x00010020);
1043 kernel_data->v[0xc9c >> 2] = htonl(0x00200001);
1044 kernel_data->v[0xca0 >> 2] = htonl(0x00010000);
1045 kernel_data->v[0xcb0 >> 2] = htonl(RAMBase);
1046 kernel_data->v[0xcb4 >> 2] = htonl(RAMSize);
1047 kernel_data->v[0xf80 >> 2] = htonl(PVR);
1048 kernel_data->v[0xf84 >> 2] = htonl(CPUClockSpeed); // clock-frequency
1049 kernel_data->v[0xf88 >> 2] = htonl(BusClockSpeed); // bus-frequency
1050 kernel_data->v[0xf8c >> 2] = htonl(TimebaseSpeed); // timebase-frequency
1051 }
1052
1053 // Initialize extra low memory
1054 D(bug("Initializing Low Memory...\n"));
1055 Mac_memset(0, 0, 0x3000);
1056 WriteMacInt32(XLM_SIGNATURE, FOURCC('B','a','a','h')); // Signature to detect SheepShaver
1057 WriteMacInt32(XLM_KERNEL_DATA, KernelDataAddr); // For trap replacement routines
1058 WriteMacInt32(XLM_PVR, PVR); // Theoretical PVR
1059 WriteMacInt32(XLM_BUS_CLOCK, BusClockSpeed); // For DriverServicesLib patch
1060 WriteMacInt16(XLM_EXEC_RETURN_OPCODE, M68K_EXEC_RETURN); // For Execute68k() (RTS from the executed 68k code will jump here and end 68k mode)
1061 WriteMacInt32(XLM_ZERO_PAGE, SheepMem::ZeroPage()); // Pointer to read-only page with all bits set to 0
1062 #if !EMULATED_PPC
1063 WriteMacInt32(XLM_TOC, (uint32)TOC); // TOC pointer of emulator
1064 #endif
1065 WriteMacInt32(XLM_ETHER_INIT, NativeFunction(NATIVE_ETHER_INIT)); // DLPI ethernet driver functions
1066 WriteMacInt32(XLM_ETHER_TERM, NativeFunction(NATIVE_ETHER_TERM));
1067 WriteMacInt32(XLM_ETHER_OPEN, NativeFunction(NATIVE_ETHER_OPEN));
1068 WriteMacInt32(XLM_ETHER_CLOSE, NativeFunction(NATIVE_ETHER_CLOSE));
1069 WriteMacInt32(XLM_ETHER_WPUT, NativeFunction(NATIVE_ETHER_WPUT));
1070 WriteMacInt32(XLM_ETHER_RSRV, NativeFunction(NATIVE_ETHER_RSRV));
1071 WriteMacInt32(XLM_VIDEO_DOIO, NativeFunction(NATIVE_VIDEO_DO_DRIVER_IO));
1072 D(bug("Low Memory initialized\n"));
1073
1074 // Start 60Hz thread
1075 tick_thread_cancel = false;
1076 tick_thread_active = (pthread_create(&tick_thread, NULL, tick_func, NULL) == 0);
1077 D(bug("Tick thread installed (%ld)\n", tick_thread));
1078
1079 // Start NVRAM watchdog thread
1080 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
1081 nvram_thread_cancel = false;
1082 nvram_thread_active = (pthread_create(&nvram_thread, NULL, nvram_func, NULL) == 0);
1083 D(bug("NVRAM thread installed (%ld)\n", nvram_thread));
1084
1085 #if !EMULATED_PPC
1086 // Install SIGILL handler
1087 sigemptyset(&sigill_action.sa_mask); // Block interrupts during ILL handling
1088 sigaddset(&sigill_action.sa_mask, SIGUSR2);
1089 sigill_action.sa_sigaction = sigill_handler;
1090 sigill_action.sa_flags = SA_ONSTACK | SA_SIGINFO;
1091 #ifdef HAVE_SIGNAL_SA_RESTORER
1092 sigill_action.sa_restorer = NULL;
1093 #endif
1094 if (sigaction(SIGILL, &sigill_action, NULL) < 0) {
1095 sprintf(str, GetString(STR_SIGILL_INSTALL_ERR), strerror(errno));
1096 ErrorAlert(str);
1097 goto quit;
1098 }
1099 #endif
1100
1101 #if !EMULATED_PPC
1102 // Install interrupt signal handler
1103 sigemptyset(&sigusr2_action.sa_mask);
1104 sigusr2_action.sa_sigaction = sigusr2_handler;
1105 sigusr2_action.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
1106 #ifdef HAVE_SIGNAL_SA_RESTORER
1107 sigusr2_action.sa_restorer = NULL;
1108 #endif
1109 if (sigaction(SIGUSR2, &sigusr2_action, NULL) < 0) {
1110 sprintf(str, GetString(STR_SIGUSR2_INSTALL_ERR), strerror(errno));
1111 ErrorAlert(str);
1112 goto quit;
1113 }
1114 #endif
1115
1116 // Get my thread ID and execute MacOS thread function
1117 emul_thread = pthread_self();
1118 D(bug("MacOS thread is %ld\n", emul_thread));
1119 emul_func(NULL);
1120
1121 quit:
1122 Quit();
1123 return 0;
1124 }
1125
1126
1127 /*
1128 * Cleanup and quit
1129 */
1130
1131 static void Quit(void)
1132 {
1133 #if EMULATED_PPC
1134 // Exit PowerPC emulation
1135 exit_emul_ppc();
1136 #endif
1137
1138 // Stop 60Hz thread
1139 if (tick_thread_active) {
1140 tick_thread_cancel = true;
1141 pthread_cancel(tick_thread);
1142 pthread_join(tick_thread, NULL);
1143 }
1144
1145 // Stop NVRAM watchdog thread
1146 if (nvram_thread_active) {
1147 nvram_thread_cancel = true;
1148 pthread_cancel(nvram_thread);
1149 pthread_join(nvram_thread, NULL);
1150 }
1151
1152 #if !EMULATED_PPC
1153 // Uninstall SIGSEGV and SIGBUS handlers
1154 sigemptyset(&sigsegv_action.sa_mask);
1155 sigsegv_action.sa_handler = SIG_DFL;
1156 sigsegv_action.sa_flags = 0;
1157 sigaction(SIGSEGV, &sigsegv_action, NULL);
1158 sigaction(SIGBUS, &sigsegv_action, NULL);
1159
1160 // Uninstall SIGILL handler
1161 sigemptyset(&sigill_action.sa_mask);
1162 sigill_action.sa_handler = SIG_DFL;
1163 sigill_action.sa_flags = 0;
1164 sigaction(SIGILL, &sigill_action, NULL);
1165
1166 // Delete stacks for signal handlers
1167 for (int i = 0; i < SIG_STACK_COUNT; i++) {
1168 void *sig_stack = sig_stacks[i].ss_sp;
1169 if (sig_stack)
1170 free(sig_stack);
1171 }
1172 #endif
1173
1174 // Save NVRAM
1175 XPRAMExit();
1176
1177 // Exit clipboard
1178 ClipExit();
1179
1180 // Exit Time Manager
1181 TimerExit();
1182
1183 // Exit serial
1184 SerialExit();
1185
1186 // Exit network
1187 EtherExit();
1188
1189 // Exit audio
1190 AudioExit();
1191
1192 // Exit ADB
1193 ADBExit();
1194
1195 // Exit video
1196 VideoExit();
1197
1198 // Exit external file system
1199 ExtFSExit();
1200
1201 // Exit drivers
1202 SCSIExit();
1203 CDROMExit();
1204 DiskExit();
1205 SonyExit();
1206
1207 // Delete thunks
1208 ThunksExit();
1209
1210 // Delete SheepShaver globals
1211 SheepMem::Exit();
1212
1213 // Delete RAM area
1214 if (ram_area_mapped)
1215 vm_mac_release(RAM_BASE, RAMSize);
1216
1217 // Delete ROM area
1218 if (rom_area_mapped)
1219 vm_mac_release(ROM_BASE, ROM_AREA_SIZE);
1220
1221 // Delete DR cache areas
1222 if (dr_emulator_area_mapped)
1223 vm_mac_release(DR_EMULATOR_BASE, DR_EMULATOR_SIZE);
1224 if (dr_cache_area_mapped)
1225 vm_mac_release(DR_CACHE_BASE, DR_CACHE_SIZE);
1226
1227 // Delete Kernel Data area
1228 kernel_data_exit();
1229
1230 // Delete Low Memory area
1231 if (lm_area_mapped)
1232 vm_mac_release(0, 0x3000);
1233
1234 // Close /dev/zero
1235 if (zero_fd > 0)
1236 close(zero_fd);
1237
1238 // Exit system routines
1239 SysExit();
1240
1241 // Exit preferences
1242 PrefsExit();
1243
1244 #ifdef ENABLE_MON
1245 // Exit mon
1246 mon_exit();
1247 #endif
1248
1249 // Close X11 server connection
1250 #ifndef USE_SDL_VIDEO
1251 if (x_display)
1252 XCloseDisplay(x_display);
1253 #endif
1254
1255 exit(0);
1256 }
1257
1258
1259 /*
1260 * Initialize Kernel Data segments
1261 */
1262
1263 #if defined(__CYGWIN__)
1264 #define WIN32_LEAN_AND_MEAN
1265 #include <windows.h>
1266
1267 static HANDLE kernel_handle; // Shared memory handle for Kernel Data
1268 static DWORD allocation_granule; // Minimum size of allocateable are (64K)
1269 static DWORD kernel_area_size; // Size of Kernel Data area
1270 #endif
1271
1272 static bool kernel_data_init(void)
1273 {
1274 #ifdef _WIN32
1275 SYSTEM_INFO si;
1276 GetSystemInfo(&si);
1277 allocation_granule = si.dwAllocationGranularity;
1278 kernel_area_size = (KERNEL_AREA_SIZE + allocation_granule - 1) & -allocation_granule;
1279
1280 char rcs[10];
1281 char str[256];
1282 LPVOID kernel_addr;
1283 kernel_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, kernel_area_size, NULL);
1284 if (kernel_handle == NULL) {
1285 sprintf(rcs, "%d", GetLastError());
1286 sprintf(str, GetString(STR_KD_SHMGET_ERR), rcs);
1287 ErrorAlert(str);
1288 return false;
1289 }
1290 kernel_addr = (LPVOID)Mac2HostAddr(KERNEL_DATA_BASE & -allocation_granule);
1291 if (MapViewOfFileEx(kernel_handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, kernel_area_size, kernel_addr) != kernel_addr) {
1292 sprintf(rcs, "%d", GetLastError());
1293 sprintf(str, GetString(STR_KD_SHMAT_ERR), rcs);
1294 ErrorAlert(str);
1295 return false;
1296 }
1297 kernel_addr = (LPVOID)Mac2HostAddr(KERNEL_DATA2_BASE & -allocation_granule);
1298 if (MapViewOfFileEx(kernel_handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, kernel_area_size, kernel_addr) != kernel_addr) {
1299 sprintf(rcs, "%d", GetLastError());
1300 sprintf(str, GetString(STR_KD2_SHMAT_ERR), rcs);
1301 ErrorAlert(str);
1302 return false;
1303 }
1304 #else
1305 kernel_area = shmget(IPC_PRIVATE, KERNEL_AREA_SIZE, 0600);
1306 if (kernel_area == -1) {
1307 sprintf(str, GetString(STR_KD_SHMGET_ERR), strerror(errno));
1308 ErrorAlert(str);
1309 return false;
1310 }
1311 if (shmat(kernel_area, Mac2HostAddr(KERNEL_DATA_BASE), 0) < 0) {
1312 sprintf(str, GetString(STR_KD_SHMAT_ERR), strerror(errno));
1313 ErrorAlert(str);
1314 return false;
1315 }
1316 if (shmat(kernel_area, Mac2HostAddr(KERNEL_DATA2_BASE), 0) < 0) {
1317 sprintf(str, GetString(STR_KD2_SHMAT_ERR), strerror(errno));
1318 ErrorAlert(str);
1319 return false;
1320 }
1321 #endif
1322 return true;
1323 }
1324
1325
1326 /*
1327 * Deallocate Kernel Data segments
1328 */
1329
1330 static void kernel_data_exit(void)
1331 {
1332 #ifdef _WIN32
1333 if (kernel_handle) {
1334 UnmapViewOfFile(Mac2HostAddr(KERNEL_DATA_BASE & -allocation_granule));
1335 UnmapViewOfFile(Mac2HostAddr(KERNEL_DATA2_BASE & -allocation_granule));
1336 CloseHandle(kernel_handle);
1337 }
1338 #else
1339 if (kernel_area >= 0) {
1340 shmdt(Mac2HostAddr(KERNEL_DATA_BASE));
1341 shmdt(Mac2HostAddr(KERNEL_DATA2_BASE));
1342 shmctl(kernel_area, IPC_RMID, NULL);
1343 }
1344 #endif
1345 }
1346
1347
1348 /*
1349 * Jump into Mac ROM, start 680x0 emulator
1350 */
1351
1352 #if EMULATED_PPC
1353 void jump_to_rom(uint32 entry)
1354 {
1355 init_emul_ppc();
1356 emul_ppc(entry);
1357 }
1358 #endif
1359
1360
1361 /*
1362 * Emulator thread function
1363 */
1364
1365 static void *emul_func(void *arg)
1366 {
1367 // We're now ready to receive signals
1368 ready_for_signals = true;
1369
1370 // Decrease priority, so more time-critical things like audio will work better
1371 nice(1);
1372
1373 // Jump to ROM boot routine
1374 D(bug("Jumping to ROM\n"));
1375 #if EMULATED_PPC
1376 jump_to_rom(ROM_BASE + 0x310000);
1377 #else
1378 jump_to_rom(ROM_BASE + 0x310000, (uint32)emulator_data);
1379 #endif
1380 D(bug("Returned from ROM\n"));
1381
1382 // We're no longer ready to receive signals
1383 ready_for_signals = false;
1384 return NULL;
1385 }
1386
1387
1388 #if !EMULATED_PPC
1389 /*
1390 * Execute 68k subroutine (must be ended with RTS)
1391 * This must only be called by the emul_thread when in EMUL_OP mode
1392 * r->a[7] is unused, the routine runs on the caller's stack
1393 */
1394
1395 void Execute68k(uint32 pc, M68kRegisters *r)
1396 {
1397 #if SAFE_EXEC_68K
1398 if (ReadMacInt32(XLM_RUN_MODE) != MODE_EMUL_OP)
1399 printf("FATAL: Execute68k() not called from EMUL_OP mode\n");
1400 if (!pthread_equal(pthread_self(), emul_thread))
1401 printf("FATAL: Execute68k() not called from emul_thread\n");
1402 #endif
1403 execute_68k(pc, r);
1404 }
1405
1406
1407 /*
1408 * Execute 68k A-Trap from EMUL_OP routine
1409 * r->a[7] is unused, the routine runs on the caller's stack
1410 */
1411
1412 void Execute68kTrap(uint16 trap, M68kRegisters *r)
1413 {
1414 uint16 proc[2] = {trap, M68K_RTS};
1415 Execute68k((uint32)proc, r);
1416 }
1417 #endif
1418
1419
1420 /*
1421 * Quit emulator (cause return from jump_to_rom)
1422 */
1423
1424 void QuitEmulator(void)
1425 {
1426 #if EMULATED_PPC
1427 Quit();
1428 #else
1429 quit_emulator();
1430 #endif
1431 }
1432
1433
1434 /*
1435 * Pause/resume emulator
1436 */
1437
1438 void PauseEmulator(void)
1439 {
1440 pthread_kill(emul_thread, SIGSTOP);
1441 }
1442
1443 void ResumeEmulator(void)
1444 {
1445 pthread_kill(emul_thread, SIGCONT);
1446 }
1447
1448
1449 /*
1450 * Dump 68k registers
1451 */
1452
1453 void Dump68kRegs(M68kRegisters *r)
1454 {
1455 // Display 68k registers
1456 for (int i=0; i<8; i++) {
1457 printf("d%d: %08x", i, r->d[i]);
1458 if (i == 3 || i == 7)
1459 printf("\n");
1460 else
1461 printf(", ");
1462 }
1463 for (int i=0; i<8; i++) {
1464 printf("a%d: %08x", i, r->a[i]);
1465 if (i == 3 || i == 7)
1466 printf("\n");
1467 else
1468 printf(", ");
1469 }
1470 }
1471
1472
1473 /*
1474 * Make code executable
1475 */
1476
1477 void MakeExecutable(int dummy, uint32 start, uint32 length)
1478 {
1479 if ((start >= ROM_BASE) && (start < (ROM_BASE + ROM_SIZE)))
1480 return;
1481 #if EMULATED_PPC
1482 FlushCodeCache(start, start + length);
1483 #else
1484 flush_icache_range(start, (void *)(start + length));
1485 #endif
1486 }
1487
1488
1489 /*
1490 * Patch things after system startup (gets called by disk driver accRun routine)
1491 */
1492
1493 void PatchAfterStartup(void)
1494 {
1495 ExecuteNative(NATIVE_VIDEO_INSTALL_ACCEL);
1496 InstallExtFS();
1497 }
1498
1499
1500 /*
1501 * NVRAM watchdog thread (saves NVRAM every minute)
1502 */
1503
1504 static void nvram_watchdog(void)
1505 {
1506 if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
1507 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
1508 SaveXPRAM();
1509 }
1510 }
1511
1512 static void *nvram_func(void *arg)
1513 {
1514 while (!nvram_thread_cancel) {
1515 for (int i=0; i<60 && !nvram_thread_cancel; i++)
1516 Delay_usec(999999); // Only wait 1 second so we quit promptly when nvram_thread_cancel becomes true
1517 nvram_watchdog();
1518 }
1519 return NULL;
1520 }
1521
1522
1523 /*
1524 * 60Hz thread (really 60.15Hz)
1525 */
1526
1527 static void *tick_func(void *arg)
1528 {
1529 int tick_counter = 0;
1530 uint64 start = GetTicks_usec();
1531 int64 ticks = 0;
1532 uint64 next = GetTicks_usec();
1533
1534 while (!tick_thread_cancel) {
1535
1536 // Wait
1537 next += 16625;
1538 int64 delay = next - GetTicks_usec();
1539 if (delay > 0)
1540 Delay_usec(delay);
1541 else if (delay < -16625)
1542 next = GetTicks_usec();
1543 ticks++;
1544
1545 #if !EMULATED_PPC
1546 // Did we crash?
1547 if (emul_thread_fatal) {
1548
1549 // Yes, dump registers
1550 sigregs *r = &sigsegv_regs;
1551 char str[256];
1552 if (crash_reason == NULL)
1553 crash_reason = "SIGSEGV";
1554 sprintf(str, "%s\n"
1555 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
1556 " xer %08lx cr %08lx \n"
1557 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
1558 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
1559 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
1560 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
1561 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
1562 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
1563 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
1564 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
1565 crash_reason,
1566 r->nip, r->link, r->ctr, r->msr,
1567 r->xer, r->ccr,
1568 r->gpr[0], r->gpr[1], r->gpr[2], r->gpr[3],
1569 r->gpr[4], r->gpr[5], r->gpr[6], r->gpr[7],
1570 r->gpr[8], r->gpr[9], r->gpr[10], r->gpr[11],
1571 r->gpr[12], r->gpr[13], r->gpr[14], r->gpr[15],
1572 r->gpr[16], r->gpr[17], r->gpr[18], r->gpr[19],
1573 r->gpr[20], r->gpr[21], r->gpr[22], r->gpr[23],
1574 r->gpr[24], r->gpr[25], r->gpr[26], r->gpr[27],
1575 r->gpr[28], r->gpr[29], r->gpr[30], r->gpr[31]);
1576 printf(str);
1577 VideoQuitFullScreen();
1578
1579 #ifdef ENABLE_MON
1580 // Start up mon in real-mode
1581 printf("Welcome to the sheep factory.\n");
1582 char *arg[4] = {"mon", "-m", "-r", NULL};
1583 mon(3, arg);
1584 #endif
1585 return NULL;
1586 }
1587 #endif
1588
1589 // Pseudo Mac 1Hz interrupt, update local time
1590 if (++tick_counter > 60) {
1591 tick_counter = 0;
1592 WriteMacInt32(0x20c, TimerDateTime());
1593 }
1594
1595 // Trigger 60Hz interrupt
1596 if (ReadMacInt32(XLM_IRQ_NEST) == 0) {
1597 SetInterruptFlag(INTFLAG_VIA);
1598 TriggerInterrupt();
1599 }
1600 }
1601
1602 uint64 end = GetTicks_usec();
1603 D(bug("%Ld ticks in %Ld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
1604 return NULL;
1605 }
1606
1607
1608 /*
1609 * Pthread configuration
1610 */
1611
1612 void Set_pthread_attr(pthread_attr_t *attr, int priority)
1613 {
1614 #ifdef HAVE_PTHREADS
1615 pthread_attr_init(attr);
1616 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
1617 // Some of these only work for superuser
1618 if (geteuid() == 0) {
1619 pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
1620 pthread_attr_setschedpolicy(attr, SCHED_FIFO);
1621 struct sched_param fifo_param;
1622 fifo_param.sched_priority = ((sched_get_priority_min(SCHED_FIFO) +
1623 sched_get_priority_max(SCHED_FIFO)) / 2 +
1624 priority);
1625 pthread_attr_setschedparam(attr, &fifo_param);
1626 }
1627 if (pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) != 0) {
1628 #ifdef PTHREAD_SCOPE_BOUND_NP
1629 // If system scope is not available (eg. we're not running
1630 // with CAP_SCHED_MGT capability on an SGI box), try bound
1631 // scope. It exposes pthread scheduling to the kernel,
1632 // without setting realtime priority.
1633 pthread_attr_setscope(attr, PTHREAD_SCOPE_BOUND_NP);
1634 #endif
1635 }
1636 #endif
1637 #endif
1638 }
1639
1640
1641 /*
1642 * Mutexes
1643 */
1644
1645 #ifdef HAVE_PTHREADS
1646
1647 struct B2_mutex {
1648 B2_mutex() {
1649 pthread_mutexattr_t attr;
1650 pthread_mutexattr_init(&attr);
1651 // Initialize the mutex for priority inheritance --
1652 // required for accurate timing.
1653 #if defined(HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL) && !defined(__CYGWIN__)
1654 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
1655 #endif
1656 #if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL)
1657 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
1658 #endif
1659 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
1660 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
1661 #endif
1662 pthread_mutex_init(&m, &attr);
1663 pthread_mutexattr_destroy(&attr);
1664 }
1665 ~B2_mutex() {
1666 pthread_mutex_trylock(&m); // Make sure it's locked before
1667 pthread_mutex_unlock(&m); // unlocking it.
1668 pthread_mutex_destroy(&m);
1669 }
1670 pthread_mutex_t m;
1671 };
1672
1673 B2_mutex *B2_create_mutex(void)
1674 {
1675 return new B2_mutex;
1676 }
1677
1678 void B2_lock_mutex(B2_mutex *mutex)
1679 {
1680 pthread_mutex_lock(&mutex->m);
1681 }
1682
1683 void B2_unlock_mutex(B2_mutex *mutex)
1684 {
1685 pthread_mutex_unlock(&mutex->m);
1686 }
1687
1688 void B2_delete_mutex(B2_mutex *mutex)
1689 {
1690 delete mutex;
1691 }
1692
1693 #else
1694
1695 struct B2_mutex {
1696 int dummy;
1697 };
1698
1699 B2_mutex *B2_create_mutex(void)
1700 {
1701 return new B2_mutex;
1702 }
1703
1704 void B2_lock_mutex(B2_mutex *mutex)
1705 {
1706 }
1707
1708 void B2_unlock_mutex(B2_mutex *mutex)
1709 {
1710 }
1711
1712 void B2_delete_mutex(B2_mutex *mutex)
1713 {
1714 delete mutex;
1715 }
1716
1717 #endif
1718
1719
1720 /*
1721 * Trigger signal USR2 from another thread
1722 */
1723
1724 #if !EMULATED_PPC
1725 void TriggerInterrupt(void)
1726 {
1727 if (ready_for_signals)
1728 pthread_kill(emul_thread, SIGUSR2);
1729 }
1730 #endif
1731
1732
1733 /*
1734 * Interrupt flags (must be handled atomically!)
1735 */
1736
1737 volatile uint32 InterruptFlags = 0;
1738
1739 void SetInterruptFlag(uint32 flag)
1740 {
1741 atomic_or((int *)&InterruptFlags, flag);
1742 }
1743
1744 void ClearInterruptFlag(uint32 flag)
1745 {
1746 atomic_and((int *)&InterruptFlags, ~flag);
1747 }
1748
1749
1750 /*
1751 * Disable interrupts
1752 */
1753
1754 void DisableInterrupt(void)
1755 {
1756 #if EMULATED_PPC
1757 WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) + 1);
1758 #else
1759 atomic_add((int *)XLM_IRQ_NEST, 1);
1760 #endif
1761 }
1762
1763
1764 /*
1765 * Enable interrupts
1766 */
1767
1768 void EnableInterrupt(void)
1769 {
1770 #if EMULATED_PPC
1771 WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) - 1);
1772 #else
1773 atomic_add((int *)XLM_IRQ_NEST, -1);
1774 #endif
1775 }
1776
1777
1778 /*
1779 * USR2 handler
1780 */
1781
1782 #if !EMULATED_PPC
1783 static void sigusr2_handler(int sig, siginfo_t *sip, void *scp)
1784 {
1785 machine_regs *r = MACHINE_REGISTERS(scp);
1786
1787 #ifdef USE_SDL_VIDEO
1788 // We must fill in the events queue in the same thread that did call SDL_SetVideoMode()
1789 SDL_PumpEvents();
1790 #endif
1791
1792 // Do nothing if interrupts are disabled
1793 if (*(int32 *)XLM_IRQ_NEST > 0)
1794 return;
1795
1796 // Disable MacOS stack sniffer
1797 WriteMacInt32(0x110, 0);
1798
1799 // Interrupt action depends on current run mode
1800 switch (ReadMacInt32(XLM_RUN_MODE)) {
1801 case MODE_68K:
1802 // 68k emulator active, trigger 68k interrupt level 1
1803 WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1);
1804 r->cr() |= ntohl(kernel_data->v[0x674 >> 2]);
1805 break;
1806
1807 #if INTERRUPTS_IN_NATIVE_MODE
1808 case MODE_NATIVE:
1809 // 68k emulator inactive, in nanokernel?
1810 if (r->gpr(1) != KernelDataAddr) {
1811
1812 // Set extra stack for nested interrupts
1813 sig_stack_acquire();
1814
1815 // Prepare for 68k interrupt level 1
1816 WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1);
1817 WriteMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc, ReadMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc) | ntohl(kernel_data->v[0x674 >> 2]));
1818
1819 // Execute nanokernel interrupt routine (this will activate the 68k emulator)
1820 DisableInterrupt();
1821 if (ROMType == ROMTYPE_NEWWORLD)
1822 ppc_interrupt(ROM_BASE + 0x312b1c, KernelDataAddr);
1823 else
1824 ppc_interrupt(ROM_BASE + 0x312a3c, KernelDataAddr);
1825
1826 // Reset normal signal stack
1827 sig_stack_release();
1828 }
1829 break;
1830 #endif
1831
1832 #if INTERRUPTS_IN_EMUL_OP_MODE
1833 case MODE_EMUL_OP:
1834 // 68k emulator active, within EMUL_OP routine, execute 68k interrupt routine directly when interrupt level is 0
1835 if ((ReadMacInt32(XLM_68K_R25) & 7) == 0) {
1836
1837 // Set extra stack for SIGSEGV handler
1838 sig_stack_acquire();
1839 #if 1
1840 // Execute full 68k interrupt routine
1841 M68kRegisters r;
1842 uint32 old_r25 = ReadMacInt32(XLM_68K_R25); // Save interrupt level
1843 WriteMacInt32(XLM_68K_R25, 0x21); // Execute with interrupt level 1
1844 static const uint16 proc[] = {
1845 0x3f3c, 0x0000, // move.w #$0000,-(sp) (fake format word)
1846 0x487a, 0x000a, // pea @1(pc) (return address)
1847 0x40e7, // move sr,-(sp) (saved SR)
1848 0x2078, 0x0064, // move.l $64,a0
1849 0x4ed0, // jmp (a0)
1850 M68K_RTS // @1
1851 };
1852 Execute68k((uint32)proc, &r);
1853 WriteMacInt32(XLM_68K_R25, old_r25); // Restore interrupt level
1854 #else
1855 // Only update cursor
1856 if (HasMacStarted()) {
1857 if (InterruptFlags & INTFLAG_VIA) {
1858 ClearInterruptFlag(INTFLAG_VIA);
1859 ADBInterrupt();
1860 ExecuteNative(NATIVE_VIDEO_VBL);
1861 }
1862 }
1863 #endif
1864 // Reset normal signal stack
1865 sig_stack_release();
1866 }
1867 break;
1868 #endif
1869 }
1870 }
1871 #endif
1872
1873
1874 /*
1875 * SIGSEGV handler
1876 */
1877
1878 #if !EMULATED_PPC
1879 static void sigsegv_handler(int sig, siginfo_t *sip, void *scp)
1880 {
1881 machine_regs *r = MACHINE_REGISTERS(scp);
1882
1883 // Get effective address
1884 uint32 addr = r->dar();
1885
1886 #if ENABLE_VOSF
1887 // Handle screen fault.
1888 extern bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction);
1889 if (Screen_fault_handler((sigsegv_address_t)addr, (sigsegv_address_t)r->pc()))
1890 return;
1891 #endif
1892
1893 num_segv++;
1894
1895 // Fault in Mac ROM or RAM or DR Cache?
1896 bool mac_fault = (r->pc() >= ROM_BASE) && (r->pc() < (ROM_BASE + ROM_AREA_SIZE)) || (r->pc() >= RAMBase) && (r->pc() < (RAMBase + RAMSize)) || (r->pc() >= DR_CACHE_BASE && r->pc() < (DR_CACHE_BASE + DR_CACHE_SIZE));
1897 if (mac_fault) {
1898
1899 // "VM settings" during MacOS 8 installation
1900 if (r->pc() == ROM_BASE + 0x488160 && r->gpr(20) == 0xf8000000) {
1901 r->pc() += 4;
1902 r->gpr(8) = 0;
1903 return;
1904
1905 // MacOS 8.5 installation
1906 } else if (r->pc() == ROM_BASE + 0x488140 && r->gpr(16) == 0xf8000000) {
1907 r->pc() += 4;
1908 r->gpr(8) = 0;
1909 return;
1910
1911 // MacOS 8 serial drivers on startup
1912 } else if (r->pc() == ROM_BASE + 0x48e080 && (r->gpr(8) == 0xf3012002 || r->gpr(8) == 0xf3012000)) {
1913 r->pc() += 4;
1914 r->gpr(8) = 0;
1915 return;
1916
1917 // MacOS 8.1 serial drivers on startup
1918 } else if (r->pc() == ROM_BASE + 0x48c5e0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1919 r->pc() += 4;
1920 return;
1921 } else if (r->pc() == ROM_BASE + 0x4a10a0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1922 r->pc() += 4;
1923 return;
1924
1925 // MacOS 8.6 serial drivers on startup (with DR Cache and OldWorld ROM)
1926 } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(16) == 0xf3012002 || r->gpr(16) == 0xf3012000)) {
1927 r->pc() += 4;
1928 return;
1929 } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1930 r->pc() += 4;
1931 return;
1932 }
1933
1934 // Get opcode and divide into fields
1935 uint32 opcode = *((uint32 *)r->pc());
1936 uint32 primop = opcode >> 26;
1937 uint32 exop = (opcode >> 1) & 0x3ff;
1938 uint32 ra = (opcode >> 16) & 0x1f;
1939 uint32 rb = (opcode >> 11) & 0x1f;
1940 uint32 rd = (opcode >> 21) & 0x1f;
1941 int32 imm = (int16)(opcode & 0xffff);
1942
1943 // Analyze opcode
1944 enum {
1945 TYPE_UNKNOWN,
1946 TYPE_LOAD,
1947 TYPE_STORE
1948 } transfer_type = TYPE_UNKNOWN;
1949 enum {
1950 SIZE_UNKNOWN,
1951 SIZE_BYTE,
1952 SIZE_HALFWORD,
1953 SIZE_WORD
1954 } transfer_size = SIZE_UNKNOWN;
1955 enum {
1956 MODE_UNKNOWN,
1957 MODE_NORM,
1958 MODE_U,
1959 MODE_X,
1960 MODE_UX
1961 } addr_mode = MODE_UNKNOWN;
1962 switch (primop) {
1963 case 31:
1964 switch (exop) {
1965 case 23: // lwzx
1966 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1967 case 55: // lwzux
1968 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1969 case 87: // lbzx
1970 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1971 case 119: // lbzux
1972 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1973 case 151: // stwx
1974 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1975 case 183: // stwux
1976 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1977 case 215: // stbx
1978 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1979 case 247: // stbux
1980 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1981 case 279: // lhzx
1982 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1983 case 311: // lhzux
1984 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1985 case 343: // lhax
1986 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1987 case 375: // lhaux
1988 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1989 case 407: // sthx
1990 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1991 case 439: // sthux
1992 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1993 }
1994 break;
1995
1996 case 32: // lwz
1997 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1998 case 33: // lwzu
1999 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
2000 case 34: // lbz
2001 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
2002 case 35: // lbzu
2003 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
2004 case 36: // stw
2005 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
2006 case 37: // stwu
2007 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
2008 case 38: // stb
2009 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
2010 case 39: // stbu
2011 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
2012 case 40: // lhz
2013 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
2014 case 41: // lhzu
2015 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
2016 case 42: // lha
2017 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
2018 case 43: // lhau
2019 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
2020 case 44: // sth
2021 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
2022 case 45: // sthu
2023 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
2024 #if EMULATE_UNALIGNED_LOADSTORE_MULTIPLE
2025 case 46: // lmw
2026 if ((addr % 4) != 0) {
2027 uint32 ea = addr;
2028 D(bug("WARNING: unaligned lmw to EA=%08x from IP=%08x\n", ea, r->pc()));
2029 for (int i = rd; i <= 31; i++) {
2030 r->gpr(i) = ReadMacInt32(ea);
2031 ea += 4;
2032 }
2033 r->pc() += 4;
2034 goto rti;
2035 }
2036 break;
2037 case 47: // stmw
2038 if ((addr % 4) != 0) {
2039 uint32 ea = addr;
2040 D(bug("WARNING: unaligned stmw to EA=%08x from IP=%08x\n", ea, r->pc()));
2041 for (int i = rd; i <= 31; i++) {
2042 WriteMacInt32(ea, r->gpr(i));
2043 ea += 4;
2044 }
2045 r->pc() += 4;
2046 goto rti;
2047 }
2048 break;
2049 #endif
2050 }
2051
2052 // Ignore ROM writes (including to the zero page, which is read-only)
2053 if (transfer_type == TYPE_STORE &&
2054 ((addr >= ROM_BASE && addr < ROM_BASE + ROM_SIZE) ||
2055 (addr >= SheepMem::ZeroPage() && addr < SheepMem::ZeroPage() + SheepMem::PageSize()))) {
2056 // 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->pc()));
2057 if (addr_mode == MODE_U || addr_mode == MODE_UX)
2058 r->gpr(ra) = addr;
2059 r->pc() += 4;
2060 goto rti;
2061 }
2062
2063 // Ignore illegal memory accesses?
2064 if (PrefsFindBool("ignoresegv")) {
2065 if (addr_mode == MODE_U || addr_mode == MODE_UX)
2066 r->gpr(ra) = addr;
2067 if (transfer_type == TYPE_LOAD)
2068 r->gpr(rd) = 0;
2069 r->pc() += 4;
2070 goto rti;
2071 }
2072
2073 // In GUI mode, show error alert
2074 if (!PrefsFindBool("nogui")) {
2075 char str[256];
2076 if (transfer_type == TYPE_LOAD || transfer_type == TYPE_STORE)
2077 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->pc(), r->gpr(24), r->gpr(1));
2078 else
2079 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode);
2080 ErrorAlert(str);
2081 QuitEmulator();
2082 return;
2083 }
2084 }
2085
2086 // For all other errors, jump into debugger (sort of...)
2087 crash_reason = (sig == SIGBUS) ? "SIGBUS" : "SIGSEGV";
2088 if (!ready_for_signals) {
2089 printf("%s\n");
2090 printf(" sigcontext %p, machine_regs %p\n", scp, r);
2091 printf(
2092 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
2093 " xer %08lx cr %08lx \n"
2094 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
2095 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
2096 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
2097 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
2098 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
2099 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
2100 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
2101 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
2102 crash_reason,
2103 r->pc(), r->lr(), r->ctr(), r->msr(),
2104 r->xer(), r->cr(),
2105 r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3),
2106 r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7),
2107 r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11),
2108 r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15),
2109 r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19),
2110 r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23),
2111 r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27),
2112 r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31));
2113 exit(1);
2114 QuitEmulator();
2115 return;
2116 } else {
2117 // We crashed. Save registers, tell tick thread and loop forever
2118 build_sigregs(&sigsegv_regs, r);
2119 emul_thread_fatal = true;
2120 for (;;) ;
2121 }
2122 rti:;
2123 }
2124
2125
2126 /*
2127 * SIGILL handler
2128 */
2129
2130 static void sigill_handler(int sig, siginfo_t *sip, void *scp)
2131 {
2132 machine_regs *r = MACHINE_REGISTERS(scp);
2133 char str[256];
2134
2135 // Fault in Mac ROM or RAM?
2136 bool mac_fault = (r->pc() >= ROM_BASE) && (r->pc() < (ROM_BASE + ROM_AREA_SIZE)) || (r->pc() >= RAMBase) && (r->pc() < (RAMBase + RAMSize));
2137 if (mac_fault) {
2138
2139 // Get opcode and divide into fields
2140 uint32 opcode = *((uint32 *)r->pc());
2141 uint32 primop = opcode >> 26;
2142 uint32 exop = (opcode >> 1) & 0x3ff;
2143 uint32 ra = (opcode >> 16) & 0x1f;
2144 uint32 rb = (opcode >> 11) & 0x1f;
2145 uint32 rd = (opcode >> 21) & 0x1f;
2146 int32 imm = (int16)(opcode & 0xffff);
2147
2148 switch (primop) {
2149 case 9: // POWER instructions
2150 case 22:
2151 power_inst: sprintf(str, GetString(STR_POWER_INSTRUCTION_ERR), r->pc(), r->gpr(1), opcode);
2152 ErrorAlert(str);
2153 QuitEmulator();
2154 return;
2155
2156 case 31:
2157 switch (exop) {
2158 case 83: // mfmsr
2159 r->gpr(rd) = 0xf072;
2160 r->pc() += 4;
2161 goto rti;
2162
2163 case 210: // mtsr
2164 case 242: // mtsrin
2165 case 306: // tlbie
2166 r->pc() += 4;
2167 goto rti;
2168
2169 case 339: { // mfspr
2170 int spr = ra | (rb << 5);
2171 switch (spr) {
2172 case 0: // MQ
2173 case 22: // DEC
2174 case 952: // MMCR0
2175 case 953: // PMC1
2176 case 954: // PMC2
2177 case 955: // SIA
2178 case 956: // MMCR1
2179 case 957: // PMC3
2180 case 958: // PMC4
2181 case 959: // SDA
2182 r->pc() += 4;
2183 goto rti;
2184 case 25: // SDR1
2185 r->gpr(rd) = 0xdead001f;
2186 r->pc() += 4;
2187 goto rti;
2188 case 287: // PVR
2189 r->gpr(rd) = PVR;
2190 r->pc() += 4;
2191 goto rti;
2192 }
2193 break;
2194 }
2195
2196 case 467: { // mtspr
2197 int spr = ra | (rb << 5);
2198 switch (spr) {
2199 case 0: // MQ
2200 case 22: // DEC
2201 case 275: // SPRG3
2202 case 528: // IBAT0U
2203 case 529: // IBAT0L
2204 case 530: // IBAT1U
2205 case 531: // IBAT1L
2206 case 532: // IBAT2U
2207 case 533: // IBAT2L
2208 case 534: // IBAT3U
2209 case 535: // IBAT3L
2210 case 536: // DBAT0U
2211 case 537: // DBAT0L
2212 case 538: // DBAT1U
2213 case 539: // DBAT1L
2214 case 540: // DBAT2U
2215 case 541: // DBAT2L
2216 case 542: // DBAT3U
2217 case 543: // DBAT3L
2218 case 952: // MMCR0
2219 case 953: // PMC1
2220 case 954: // PMC2
2221 case 955: // SIA
2222 case 956: // MMCR1
2223 case 957: // PMC3
2224 case 958: // PMC4
2225 case 959: // SDA
2226 r->pc() += 4;
2227 goto rti;
2228 }
2229 break;
2230 }
2231
2232 case 29: case 107: case 152: case 153: // POWER instructions
2233 case 184: case 216: case 217: case 248:
2234 case 264: case 277: case 331: case 360:
2235 case 363: case 488: case 531: case 537:
2236 case 541: case 664: case 665: case 696:
2237 case 728: case 729: case 760: case 920:
2238 case 921: case 952:
2239 goto power_inst;
2240 }
2241 }
2242
2243 // In GUI mode, show error alert
2244 if (!PrefsFindBool("nogui")) {
2245 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode);
2246 ErrorAlert(str);
2247 QuitEmulator();
2248 return;
2249 }
2250 }
2251
2252 // For all other errors, jump into debugger (sort of...)
2253 crash_reason = "SIGILL";
2254 if (!ready_for_signals) {
2255 printf("%s\n");
2256 printf(" sigcontext %p, machine_regs %p\n", scp, r);
2257 printf(
2258 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
2259 " xer %08lx cr %08lx \n"
2260 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
2261 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
2262 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
2263 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
2264 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
2265 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
2266 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
2267 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
2268 crash_reason,
2269 r->pc(), r->lr(), r->ctr(), r->msr(),
2270 r->xer(), r->cr(),
2271 r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3),
2272 r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7),
2273 r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11),
2274 r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15),
2275 r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19),
2276 r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23),
2277 r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27),
2278 r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31));
2279 exit(1);
2280 QuitEmulator();
2281 return;
2282 } else {
2283 // We crashed. Save registers, tell tick thread and loop forever
2284 build_sigregs(&sigsegv_regs, r);
2285 emul_thread_fatal = true;
2286 for (;;) ;
2287 }
2288 rti:;
2289 }
2290 #endif
2291
2292
2293 /*
2294 * Helpers to share 32-bit addressable data with MacOS
2295 */
2296
2297 bool SheepMem::Init(void)
2298 {
2299 // Size of a native page
2300 page_size = getpagesize();
2301
2302 // Allocate SheepShaver globals
2303 proc = base;
2304 if (vm_mac_acquire(base, size) < 0)
2305 return false;
2306
2307 // Allocate page with all bits set to 0, right in the middle
2308 // This is also used to catch undesired overlaps between proc and data areas
2309 zero_page = proc + (size / 2);
2310 Mac_memset(zero_page, 0, page_size);
2311 if (vm_protect(Mac2HostAddr(zero_page), page_size, VM_PAGE_READ) < 0)
2312 return false;
2313
2314 #if EMULATED_PPC
2315 // Allocate alternate stack for PowerPC interrupt routine
2316 sig_stack = base + size;
2317 if (vm_mac_acquire(sig_stack, SIG_STACK_SIZE) < 0)
2318 return false;
2319 #endif
2320
2321 data = base + size;
2322 return true;
2323 }
2324
2325 void SheepMem::Exit(void)
2326 {
2327 if (data) {
2328 // Delete SheepShaver globals
2329 vm_mac_release(base, size);
2330
2331 #if EMULATED_PPC
2332 // Delete alternate stack for PowerPC interrupt routine
2333 vm_mac_release(sig_stack, SIG_STACK_SIZE);
2334 #endif
2335 }
2336 }
2337
2338
2339 /*
2340 * Display alert
2341 */
2342
2343 #ifdef ENABLE_GTK
2344 static void dl_destroyed(void)
2345 {
2346 gtk_main_quit();
2347 }
2348
2349 static void dl_quit(GtkWidget *dialog)
2350 {
2351 gtk_widget_destroy(dialog);
2352 }
2353
2354 void display_alert(int title_id, int prefix_id, int button_id, const char *text)
2355 {
2356 char str[256];
2357 sprintf(str, GetString(prefix_id), text);
2358
2359 GtkWidget *dialog = gtk_dialog_new();
2360 gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
2361 gtk_container_border_width(GTK_CONTAINER(dialog), 5);
2362 gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
2363 gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
2364
2365 GtkWidget *label = gtk_label_new(str);
2366 gtk_widget_show(label);
2367 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
2368
2369 GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
2370 gtk_widget_show(button);
2371 gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
2372 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
2373 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2374 gtk_widget_grab_default(button);
2375 gtk_widget_show(dialog);
2376
2377 gtk_main();
2378 }
2379 #endif
2380
2381
2382 /*
2383 * Display error alert
2384 */
2385
2386 void ErrorAlert(const char *text)
2387 {
2388 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
2389 if (PrefsFindBool("nogui") || x_display == NULL) {
2390 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
2391 return;
2392 }
2393 VideoQuitFullScreen();
2394 display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
2395 #else
2396 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
2397 #endif
2398 }
2399
2400
2401 /*
2402 * Display warning alert
2403 */
2404
2405 void WarningAlert(const char *text)
2406 {
2407 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
2408 if (PrefsFindBool("nogui") || x_display == NULL) {
2409 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2410 return;
2411 }
2412 display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
2413 #else
2414 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2415 #endif
2416 }
2417
2418
2419 /*
2420 * Display choice alert
2421 */
2422
2423 bool ChoiceAlert(const char *text, const char *pos, const char *neg)
2424 {
2425 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2426 return false; //!!
2427 }