ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/main_unix.cpp
Revision: 1.66
Committed: 2005-06-28T16:50:30Z (18 years, 11 months ago) by gbeauche
Branch: MAIN
Changes since 1.65: +3 -3 lines
Log Message:
Clean-ups: comments, ticks per sec output, don't restore r13 twice.

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     if (ready_for_signals)
1517     pthread_kill(emul_thread, SIGUSR2);
1518     }
1519 gbeauche 1.7 #endif
1520 cebix 1.1
1521    
1522     /*
1523     * Interrupt flags (must be handled atomically!)
1524     */
1525    
1526     volatile uint32 InterruptFlags = 0;
1527    
1528     void SetInterruptFlag(uint32 flag)
1529     {
1530     atomic_or((int *)&InterruptFlags, flag);
1531     }
1532    
1533     void ClearInterruptFlag(uint32 flag)
1534     {
1535     atomic_and((int *)&InterruptFlags, ~flag);
1536     }
1537    
1538    
1539     /*
1540     * Disable interrupts
1541     */
1542    
1543     void DisableInterrupt(void)
1544     {
1545 gbeauche 1.41 #if EMULATED_PPC
1546     WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) + 1);
1547     #else
1548 gbeauche 1.7 atomic_add((int *)XLM_IRQ_NEST, 1);
1549 gbeauche 1.41 #endif
1550 cebix 1.1 }
1551    
1552    
1553     /*
1554     * Enable interrupts
1555     */
1556    
1557     void EnableInterrupt(void)
1558     {
1559 gbeauche 1.41 #if EMULATED_PPC
1560     WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) - 1);
1561     #else
1562 gbeauche 1.7 atomic_add((int *)XLM_IRQ_NEST, -1);
1563 gbeauche 1.41 #endif
1564 cebix 1.1 }
1565    
1566    
1567     /*
1568     * USR2 handler
1569     */
1570    
1571 gbeauche 1.35 #if !EMULATED_PPC
1572 gbeauche 1.65 void sigusr2_handler(int sig, siginfo_t *sip, void *scp)
1573 cebix 1.1 {
1574 gbeauche 1.26 machine_regs *r = MACHINE_REGISTERS(scp);
1575 cebix 1.1
1576 gbeauche 1.42 #ifdef USE_SDL_VIDEO
1577     // We must fill in the events queue in the same thread that did call SDL_SetVideoMode()
1578     SDL_PumpEvents();
1579     #endif
1580    
1581 cebix 1.1 // Do nothing if interrupts are disabled
1582     if (*(int32 *)XLM_IRQ_NEST > 0)
1583     return;
1584    
1585 gbeauche 1.60 #ifdef SYSTEM_CLOBBERS_R2
1586     // Restore pointer to Thread Local Storage
1587     set_r2(TOC);
1588     #endif
1589     #ifdef SYSTEM_CLOBBERS_R13
1590     // Restore pointer to .sdata section
1591     set_r13(R13);
1592     #endif
1593    
1594 cebix 1.1 // Disable MacOS stack sniffer
1595     WriteMacInt32(0x110, 0);
1596    
1597     // Interrupt action depends on current run mode
1598     switch (ReadMacInt32(XLM_RUN_MODE)) {
1599     case MODE_68K:
1600     // 68k emulator active, trigger 68k interrupt level 1
1601     WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1);
1602 gbeauche 1.26 r->cr() |= ntohl(kernel_data->v[0x674 >> 2]);
1603 cebix 1.1 break;
1604    
1605     #if INTERRUPTS_IN_NATIVE_MODE
1606     case MODE_NATIVE:
1607     // 68k emulator inactive, in nanokernel?
1608 gbeauche 1.26 if (r->gpr(1) != KernelDataAddr) {
1609 gbeauche 1.33
1610 gbeauche 1.65 // Set extra stack for SIGSEGV handler
1611     sigaltstack(&extra_stack, NULL);
1612 gbeauche 1.33
1613 cebix 1.1 // Prepare for 68k interrupt level 1
1614     WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1);
1615     WriteMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc, ReadMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc) | ntohl(kernel_data->v[0x674 >> 2]));
1616    
1617     // Execute nanokernel interrupt routine (this will activate the 68k emulator)
1618 gbeauche 1.33 DisableInterrupt();
1619 cebix 1.1 if (ROMType == ROMTYPE_NEWWORLD)
1620     ppc_interrupt(ROM_BASE + 0x312b1c, KernelDataAddr);
1621     else
1622     ppc_interrupt(ROM_BASE + 0x312a3c, KernelDataAddr);
1623 gbeauche 1.33
1624 gbeauche 1.65 // Reset normal stack
1625     sigaltstack(&sig_stack, NULL);
1626 cebix 1.1 }
1627     break;
1628     #endif
1629    
1630     #if INTERRUPTS_IN_EMUL_OP_MODE
1631     case MODE_EMUL_OP:
1632     // 68k emulator active, within EMUL_OP routine, execute 68k interrupt routine directly when interrupt level is 0
1633     if ((ReadMacInt32(XLM_68K_R25) & 7) == 0) {
1634    
1635     // Set extra stack for SIGSEGV handler
1636 gbeauche 1.65 sigaltstack(&extra_stack, NULL);
1637 cebix 1.1 #if 1
1638     // Execute full 68k interrupt routine
1639     M68kRegisters r;
1640     uint32 old_r25 = ReadMacInt32(XLM_68K_R25); // Save interrupt level
1641     WriteMacInt32(XLM_68K_R25, 0x21); // Execute with interrupt level 1
1642     static const uint16 proc[] = {
1643     0x3f3c, 0x0000, // move.w #$0000,-(sp) (fake format word)
1644     0x487a, 0x000a, // pea @1(pc) (return address)
1645     0x40e7, // move sr,-(sp) (saved SR)
1646     0x2078, 0x0064, // move.l $64,a0
1647     0x4ed0, // jmp (a0)
1648     M68K_RTS // @1
1649     };
1650     Execute68k((uint32)proc, &r);
1651     WriteMacInt32(XLM_68K_R25, old_r25); // Restore interrupt level
1652     #else
1653     // Only update cursor
1654     if (HasMacStarted()) {
1655     if (InterruptFlags & INTFLAG_VIA) {
1656     ClearInterruptFlag(INTFLAG_VIA);
1657     ADBInterrupt();
1658 gbeauche 1.17 ExecuteNative(NATIVE_VIDEO_VBL);
1659 cebix 1.1 }
1660     }
1661     #endif
1662 gbeauche 1.65 // Reset normal stack
1663     sigaltstack(&sig_stack, NULL);
1664 cebix 1.1 }
1665     break;
1666     #endif
1667     }
1668     }
1669 gbeauche 1.8 #endif
1670 cebix 1.1
1671    
1672     /*
1673     * SIGSEGV handler
1674     */
1675    
1676 gbeauche 1.8 #if !EMULATED_PPC
1677 gbeauche 1.26 static void sigsegv_handler(int sig, siginfo_t *sip, void *scp)
1678 cebix 1.1 {
1679 gbeauche 1.26 machine_regs *r = MACHINE_REGISTERS(scp);
1680 gbeauche 1.5
1681     // Get effective address
1682 gbeauche 1.26 uint32 addr = r->dar();
1683 gbeauche 1.5
1684 gbeauche 1.60 #ifdef SYSTEM_CLOBBERS_R2
1685     // Restore pointer to Thread Local Storage
1686     set_r2(TOC);
1687     #endif
1688     #ifdef SYSTEM_CLOBBERS_R13
1689     // Restore pointer to .sdata section
1690     set_r13(R13);
1691     #endif
1692    
1693 gbeauche 1.5 #if ENABLE_VOSF
1694     // Handle screen fault.
1695     extern bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction);
1696 gbeauche 1.26 if (Screen_fault_handler((sigsegv_address_t)addr, (sigsegv_address_t)r->pc()))
1697 gbeauche 1.5 return;
1698     #endif
1699    
1700 cebix 1.1 num_segv++;
1701    
1702 gbeauche 1.37 // Fault in Mac ROM or RAM or DR Cache?
1703     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));
1704 cebix 1.1 if (mac_fault) {
1705    
1706     // "VM settings" during MacOS 8 installation
1707 gbeauche 1.26 if (r->pc() == ROM_BASE + 0x488160 && r->gpr(20) == 0xf8000000) {
1708     r->pc() += 4;
1709     r->gpr(8) = 0;
1710 cebix 1.1 return;
1711    
1712     // MacOS 8.5 installation
1713 gbeauche 1.26 } else if (r->pc() == ROM_BASE + 0x488140 && r->gpr(16) == 0xf8000000) {
1714     r->pc() += 4;
1715     r->gpr(8) = 0;
1716 cebix 1.1 return;
1717    
1718     // MacOS 8 serial drivers on startup
1719 gbeauche 1.26 } else if (r->pc() == ROM_BASE + 0x48e080 && (r->gpr(8) == 0xf3012002 || r->gpr(8) == 0xf3012000)) {
1720     r->pc() += 4;
1721     r->gpr(8) = 0;
1722 cebix 1.1 return;
1723    
1724     // MacOS 8.1 serial drivers on startup
1725 gbeauche 1.26 } else if (r->pc() == ROM_BASE + 0x48c5e0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1726     r->pc() += 4;
1727 cebix 1.1 return;
1728 gbeauche 1.26 } else if (r->pc() == ROM_BASE + 0x4a10a0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1729     r->pc() += 4;
1730 cebix 1.1 return;
1731 gbeauche 1.37
1732     // MacOS 8.6 serial drivers on startup (with DR Cache and OldWorld ROM)
1733     } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(16) == 0xf3012002 || r->gpr(16) == 0xf3012000)) {
1734     r->pc() += 4;
1735     return;
1736     } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1737     r->pc() += 4;
1738     return;
1739 cebix 1.1 }
1740    
1741 gbeauche 1.5 // Get opcode and divide into fields
1742 gbeauche 1.26 uint32 opcode = *((uint32 *)r->pc());
1743 gbeauche 1.5 uint32 primop = opcode >> 26;
1744     uint32 exop = (opcode >> 1) & 0x3ff;
1745     uint32 ra = (opcode >> 16) & 0x1f;
1746     uint32 rb = (opcode >> 11) & 0x1f;
1747     uint32 rd = (opcode >> 21) & 0x1f;
1748     int32 imm = (int16)(opcode & 0xffff);
1749    
1750 cebix 1.1 // Analyze opcode
1751     enum {
1752     TYPE_UNKNOWN,
1753     TYPE_LOAD,
1754     TYPE_STORE
1755     } transfer_type = TYPE_UNKNOWN;
1756     enum {
1757     SIZE_UNKNOWN,
1758     SIZE_BYTE,
1759     SIZE_HALFWORD,
1760     SIZE_WORD
1761     } transfer_size = SIZE_UNKNOWN;
1762     enum {
1763     MODE_UNKNOWN,
1764     MODE_NORM,
1765     MODE_U,
1766     MODE_X,
1767     MODE_UX
1768     } addr_mode = MODE_UNKNOWN;
1769     switch (primop) {
1770     case 31:
1771     switch (exop) {
1772     case 23: // lwzx
1773     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1774     case 55: // lwzux
1775     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1776     case 87: // lbzx
1777     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1778     case 119: // lbzux
1779     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1780     case 151: // stwx
1781     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1782     case 183: // stwux
1783     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1784     case 215: // stbx
1785     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1786     case 247: // stbux
1787     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1788     case 279: // lhzx
1789     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1790     case 311: // lhzux
1791     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1792     case 343: // lhax
1793     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1794     case 375: // lhaux
1795     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1796     case 407: // sthx
1797     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1798     case 439: // sthux
1799     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1800     }
1801     break;
1802    
1803     case 32: // lwz
1804     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1805     case 33: // lwzu
1806     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
1807     case 34: // lbz
1808     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
1809     case 35: // lbzu
1810     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
1811     case 36: // stw
1812     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1813     case 37: // stwu
1814     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
1815     case 38: // stb
1816     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
1817     case 39: // stbu
1818     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
1819     case 40: // lhz
1820     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1821     case 41: // lhzu
1822     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1823     case 42: // lha
1824     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1825     case 43: // lhau
1826     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1827     case 44: // sth
1828     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1829     case 45: // sthu
1830     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1831 gbeauche 1.23 #if EMULATE_UNALIGNED_LOADSTORE_MULTIPLE
1832     case 46: // lmw
1833 gbeauche 1.27 if ((addr % 4) != 0) {
1834     uint32 ea = addr;
1835 gbeauche 1.26 D(bug("WARNING: unaligned lmw to EA=%08x from IP=%08x\n", ea, r->pc()));
1836 gbeauche 1.23 for (int i = rd; i <= 31; i++) {
1837 gbeauche 1.26 r->gpr(i) = ReadMacInt32(ea);
1838 gbeauche 1.23 ea += 4;
1839     }
1840 gbeauche 1.26 r->pc() += 4;
1841 gbeauche 1.23 goto rti;
1842     }
1843     break;
1844     case 47: // stmw
1845 gbeauche 1.27 if ((addr % 4) != 0) {
1846     uint32 ea = addr;
1847 gbeauche 1.26 D(bug("WARNING: unaligned stmw to EA=%08x from IP=%08x\n", ea, r->pc()));
1848 gbeauche 1.23 for (int i = rd; i <= 31; i++) {
1849 gbeauche 1.26 WriteMacInt32(ea, r->gpr(i));
1850 gbeauche 1.23 ea += 4;
1851     }
1852 gbeauche 1.26 r->pc() += 4;
1853 gbeauche 1.23 goto rti;
1854     }
1855     break;
1856     #endif
1857 cebix 1.1 }
1858    
1859 gbeauche 1.31 // Ignore ROM writes (including to the zero page, which is read-only)
1860     if (transfer_type == TYPE_STORE &&
1861     ((addr >= ROM_BASE && addr < ROM_BASE + ROM_SIZE) ||
1862     (addr >= SheepMem::ZeroPage() && addr < SheepMem::ZeroPage() + SheepMem::PageSize()))) {
1863 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()));
1864 cebix 1.1 if (addr_mode == MODE_U || addr_mode == MODE_UX)
1865 gbeauche 1.26 r->gpr(ra) = addr;
1866     r->pc() += 4;
1867 cebix 1.1 goto rti;
1868     }
1869    
1870     // Ignore illegal memory accesses?
1871     if (PrefsFindBool("ignoresegv")) {
1872     if (addr_mode == MODE_U || addr_mode == MODE_UX)
1873 gbeauche 1.26 r->gpr(ra) = addr;
1874 cebix 1.1 if (transfer_type == TYPE_LOAD)
1875 gbeauche 1.26 r->gpr(rd) = 0;
1876     r->pc() += 4;
1877 cebix 1.1 goto rti;
1878     }
1879    
1880     // In GUI mode, show error alert
1881     if (!PrefsFindBool("nogui")) {
1882     char str[256];
1883     if (transfer_type == TYPE_LOAD || transfer_type == TYPE_STORE)
1884 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));
1885 cebix 1.1 else
1886 gbeauche 1.26 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode);
1887 cebix 1.1 ErrorAlert(str);
1888     QuitEmulator();
1889     return;
1890     }
1891     }
1892    
1893     // For all other errors, jump into debugger (sort of...)
1894 gbeauche 1.23 crash_reason = (sig == SIGBUS) ? "SIGBUS" : "SIGSEGV";
1895 cebix 1.1 if (!ready_for_signals) {
1896 gbeauche 1.23 printf("%s\n");
1897 gbeauche 1.26 printf(" sigcontext %p, machine_regs %p\n", scp, r);
1898 cebix 1.1 printf(
1899     " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
1900     " xer %08lx cr %08lx \n"
1901     " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
1902     " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
1903     " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
1904     " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
1905     " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
1906     " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
1907     " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
1908     " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
1909 gbeauche 1.23 crash_reason,
1910 gbeauche 1.26 r->pc(), r->lr(), r->ctr(), r->msr(),
1911     r->xer(), r->cr(),
1912     r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3),
1913     r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7),
1914     r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11),
1915     r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15),
1916     r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19),
1917     r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23),
1918     r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27),
1919     r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31));
1920 cebix 1.1 exit(1);
1921     QuitEmulator();
1922     return;
1923     } else {
1924     // We crashed. Save registers, tell tick thread and loop forever
1925 gbeauche 1.26 build_sigregs(&sigsegv_regs, r);
1926 cebix 1.1 emul_thread_fatal = true;
1927     for (;;) ;
1928     }
1929     rti:;
1930     }
1931    
1932    
1933     /*
1934     * SIGILL handler
1935     */
1936    
1937 gbeauche 1.26 static void sigill_handler(int sig, siginfo_t *sip, void *scp)
1938 cebix 1.1 {
1939 gbeauche 1.26 machine_regs *r = MACHINE_REGISTERS(scp);
1940 cebix 1.1 char str[256];
1941    
1942 gbeauche 1.60 #ifdef SYSTEM_CLOBBERS_R2
1943     // Restore pointer to Thread Local Storage
1944     set_r2(TOC);
1945     #endif
1946     #ifdef SYSTEM_CLOBBERS_R13
1947     // Restore pointer to .sdata section
1948     set_r13(R13);
1949     #endif
1950    
1951 cebix 1.1 // Fault in Mac ROM or RAM?
1952 gbeauche 1.26 bool mac_fault = (r->pc() >= ROM_BASE) && (r->pc() < (ROM_BASE + ROM_AREA_SIZE)) || (r->pc() >= RAMBase) && (r->pc() < (RAMBase + RAMSize));
1953 cebix 1.1 if (mac_fault) {
1954    
1955     // Get opcode and divide into fields
1956 gbeauche 1.26 uint32 opcode = *((uint32 *)r->pc());
1957 cebix 1.1 uint32 primop = opcode >> 26;
1958     uint32 exop = (opcode >> 1) & 0x3ff;
1959     uint32 ra = (opcode >> 16) & 0x1f;
1960     uint32 rb = (opcode >> 11) & 0x1f;
1961     uint32 rd = (opcode >> 21) & 0x1f;
1962     int32 imm = (int16)(opcode & 0xffff);
1963    
1964     switch (primop) {
1965     case 9: // POWER instructions
1966     case 22:
1967 gbeauche 1.26 power_inst: sprintf(str, GetString(STR_POWER_INSTRUCTION_ERR), r->pc(), r->gpr(1), opcode);
1968 cebix 1.1 ErrorAlert(str);
1969     QuitEmulator();
1970     return;
1971    
1972     case 31:
1973     switch (exop) {
1974     case 83: // mfmsr
1975 gbeauche 1.26 r->gpr(rd) = 0xf072;
1976     r->pc() += 4;
1977 cebix 1.1 goto rti;
1978    
1979     case 210: // mtsr
1980     case 242: // mtsrin
1981     case 306: // tlbie
1982 gbeauche 1.26 r->pc() += 4;
1983 cebix 1.1 goto rti;
1984    
1985     case 339: { // mfspr
1986     int spr = ra | (rb << 5);
1987     switch (spr) {
1988     case 0: // MQ
1989     case 22: // DEC
1990     case 952: // MMCR0
1991     case 953: // PMC1
1992     case 954: // PMC2
1993     case 955: // SIA
1994     case 956: // MMCR1
1995     case 957: // PMC3
1996     case 958: // PMC4
1997     case 959: // SDA
1998 gbeauche 1.26 r->pc() += 4;
1999 cebix 1.1 goto rti;
2000     case 25: // SDR1
2001 gbeauche 1.26 r->gpr(rd) = 0xdead001f;
2002     r->pc() += 4;
2003 cebix 1.1 goto rti;
2004     case 287: // PVR
2005 gbeauche 1.26 r->gpr(rd) = PVR;
2006     r->pc() += 4;
2007 cebix 1.1 goto rti;
2008     }
2009     break;
2010     }
2011    
2012     case 467: { // mtspr
2013     int spr = ra | (rb << 5);
2014     switch (spr) {
2015     case 0: // MQ
2016     case 22: // DEC
2017     case 275: // SPRG3
2018     case 528: // IBAT0U
2019     case 529: // IBAT0L
2020     case 530: // IBAT1U
2021     case 531: // IBAT1L
2022     case 532: // IBAT2U
2023     case 533: // IBAT2L
2024     case 534: // IBAT3U
2025     case 535: // IBAT3L
2026     case 536: // DBAT0U
2027     case 537: // DBAT0L
2028     case 538: // DBAT1U
2029     case 539: // DBAT1L
2030     case 540: // DBAT2U
2031     case 541: // DBAT2L
2032     case 542: // DBAT3U
2033     case 543: // DBAT3L
2034     case 952: // MMCR0
2035     case 953: // PMC1
2036     case 954: // PMC2
2037     case 955: // SIA
2038     case 956: // MMCR1
2039     case 957: // PMC3
2040     case 958: // PMC4
2041     case 959: // SDA
2042 gbeauche 1.26 r->pc() += 4;
2043 cebix 1.1 goto rti;
2044     }
2045     break;
2046     }
2047    
2048     case 29: case 107: case 152: case 153: // POWER instructions
2049     case 184: case 216: case 217: case 248:
2050     case 264: case 277: case 331: case 360:
2051     case 363: case 488: case 531: case 537:
2052     case 541: case 664: case 665: case 696:
2053     case 728: case 729: case 760: case 920:
2054     case 921: case 952:
2055     goto power_inst;
2056     }
2057     }
2058    
2059     // In GUI mode, show error alert
2060     if (!PrefsFindBool("nogui")) {
2061 gbeauche 1.26 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode);
2062 cebix 1.1 ErrorAlert(str);
2063     QuitEmulator();
2064     return;
2065     }
2066     }
2067    
2068     // For all other errors, jump into debugger (sort of...)
2069 gbeauche 1.23 crash_reason = "SIGILL";
2070 cebix 1.1 if (!ready_for_signals) {
2071 gbeauche 1.23 printf("%s\n");
2072 gbeauche 1.26 printf(" sigcontext %p, machine_regs %p\n", scp, r);
2073 cebix 1.1 printf(
2074     " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
2075     " xer %08lx cr %08lx \n"
2076     " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
2077     " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
2078     " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
2079     " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
2080     " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
2081     " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
2082     " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
2083     " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
2084 gbeauche 1.23 crash_reason,
2085 gbeauche 1.26 r->pc(), r->lr(), r->ctr(), r->msr(),
2086     r->xer(), r->cr(),
2087     r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3),
2088     r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7),
2089     r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11),
2090     r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15),
2091     r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19),
2092     r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23),
2093     r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27),
2094     r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31));
2095 cebix 1.1 exit(1);
2096     QuitEmulator();
2097     return;
2098     } else {
2099     // We crashed. Save registers, tell tick thread and loop forever
2100 gbeauche 1.26 build_sigregs(&sigsegv_regs, r);
2101 cebix 1.1 emul_thread_fatal = true;
2102     for (;;) ;
2103     }
2104     rti:;
2105     }
2106     #endif
2107 gbeauche 1.15
2108    
2109     /*
2110     * Helpers to share 32-bit addressable data with MacOS
2111     */
2112    
2113     bool SheepMem::Init(void)
2114     {
2115 gbeauche 1.31 // Size of a native page
2116     page_size = getpagesize();
2117 gbeauche 1.20
2118     // Allocate SheepShaver globals
2119 gbeauche 1.53 proc = base;
2120     if (vm_mac_acquire(base, size) < 0)
2121 gbeauche 1.15 return false;
2122 gbeauche 1.18
2123 gbeauche 1.53 // Allocate page with all bits set to 0, right in the middle
2124     // This is also used to catch undesired overlaps between proc and data areas
2125     zero_page = proc + (size / 2);
2126     Mac_memset(zero_page, 0, page_size);
2127     if (vm_protect(Mac2HostAddr(zero_page), page_size, VM_PAGE_READ) < 0)
2128 gbeauche 1.18 return false;
2129    
2130 gbeauche 1.20 #if EMULATED_PPC
2131     // Allocate alternate stack for PowerPC interrupt routine
2132 gbeauche 1.53 sig_stack = base + size;
2133     if (vm_mac_acquire(sig_stack, SIG_STACK_SIZE) < 0)
2134 gbeauche 1.20 return false;
2135     #endif
2136    
2137 gbeauche 1.53 data = base + size;
2138 gbeauche 1.15 return true;
2139     }
2140    
2141     void SheepMem::Exit(void)
2142     {
2143 gbeauche 1.53 if (data) {
2144 gbeauche 1.20 // Delete SheepShaver globals
2145 gbeauche 1.53 vm_mac_release(base, size);
2146 gbeauche 1.20
2147     #if EMULATED_PPC
2148     // Delete alternate stack for PowerPC interrupt routine
2149 gbeauche 1.53 vm_mac_release(sig_stack, SIG_STACK_SIZE);
2150 gbeauche 1.20 #endif
2151 gbeauche 1.18 }
2152 gbeauche 1.15 }
2153 cebix 1.1
2154    
2155     /*
2156     * Display alert
2157     */
2158    
2159     #ifdef ENABLE_GTK
2160     static void dl_destroyed(void)
2161     {
2162     gtk_main_quit();
2163     }
2164    
2165     static void dl_quit(GtkWidget *dialog)
2166     {
2167     gtk_widget_destroy(dialog);
2168     }
2169    
2170     void display_alert(int title_id, int prefix_id, int button_id, const char *text)
2171     {
2172     char str[256];
2173     sprintf(str, GetString(prefix_id), text);
2174    
2175     GtkWidget *dialog = gtk_dialog_new();
2176     gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
2177     gtk_container_border_width(GTK_CONTAINER(dialog), 5);
2178     gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
2179     gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
2180    
2181     GtkWidget *label = gtk_label_new(str);
2182     gtk_widget_show(label);
2183     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
2184    
2185     GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
2186     gtk_widget_show(button);
2187     gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
2188     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
2189     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2190     gtk_widget_grab_default(button);
2191     gtk_widget_show(dialog);
2192    
2193     gtk_main();
2194     }
2195     #endif
2196    
2197    
2198     /*
2199     * Display error alert
2200     */
2201    
2202     void ErrorAlert(const char *text)
2203     {
2204 gbeauche 1.42 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
2205 cebix 1.1 if (PrefsFindBool("nogui") || x_display == NULL) {
2206     printf(GetString(STR_SHELL_ERROR_PREFIX), text);
2207     return;
2208     }
2209     VideoQuitFullScreen();
2210     display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
2211     #else
2212     printf(GetString(STR_SHELL_ERROR_PREFIX), text);
2213     #endif
2214     }
2215    
2216    
2217     /*
2218     * Display warning alert
2219     */
2220    
2221     void WarningAlert(const char *text)
2222     {
2223 gbeauche 1.42 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
2224 cebix 1.1 if (PrefsFindBool("nogui") || x_display == NULL) {
2225     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2226     return;
2227     }
2228     display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
2229     #else
2230     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2231     #endif
2232     }
2233    
2234    
2235     /*
2236     * Display choice alert
2237     */
2238    
2239     bool ChoiceAlert(const char *text, const char *pos, const char *neg)
2240     {
2241     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2242     return false; //!!
2243     }