ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/main_unix.cpp
Revision: 1.81
Committed: 2008-06-22T20:30:12Z (16 years, 4 months ago) by asvitkine
Branch: MAIN
Changes since 1.80: +5 -2 lines
Log Message:
[patch from Kelvin Delbarre] SDL's Quartz video implementation maps command
clicks to right-clicks and option-clicks to middle-clicks, a feature intended
for Mac users with single-button mice who are running SDL-based games that
require a multi-button mouse. This is unhelpful in SheepShaver, where we want
command-clicks and option-clicks to be passed through unchanged to the emulated
Mac OS. We can disable the unwanted behavior by setting an environment variable
SDL_HAS3BUTTONMOUSE intended for this very purpose.

A similar change in main_windows.cpp is NOT required, because only the Quartz
video implementation is involved.

By SDL convention, putenv is used in preference to setenv, although for Unix
platforms it doesn't matter.

File Contents

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