ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/main_unix.cpp
Revision: 1.67
Committed: 2005-06-30T10:17:58Z (19 years, 2 months ago) by gbeauche
Branch: MAIN
Changes since 1.66: +3 -1 lines
Log Message:
Improve idle wait mechanism. Now, the emulator thread can be suspended
(idle_wait) until events arrived and notified through TriggerInterrupt().
i.e. we no longer sleep a fixed amount of time on platforms that support
a thread wait/signal mechanism.

File Contents

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