ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/main_unix.cpp
Revision: 1.82
Committed: 2009-02-11T19:22:16Z (15 years, 7 months ago) by asvitkine
Branch: MAIN
Changes since 1.81: +11 -7 lines
Log Message:
[Patch from Mike Sliczniak]

This first patch gets B2 and SS to build under Leopard and Tiger.

I tested this on a 32-bit intel 10.5.6 mac like so:

B2
./autogen.sh --disable-standalone-gui --enable-vosf --enable-sdl-video --enable-sdl-audio --enable-addressing=real --without-esd --without-gtk --without-mon --without-x

SS
./autogen.sh --disable-standalone-gui --enable-vosf -enable-sdl-video --disable-sdl-audio --enable-addressing=real --without-esd --without-gtk --without-mon --without-x --enable-jit

There is also a little tweak so that you can use sdl audio in SheepShaver when building for Mac OS X.

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 asvitkine 1.82 static stack_t sig_stack; // Stack for signal handlers
229     static stack_t 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 asvitkine 1.82 // XXX: don't redefine locally?
1627     // Note: Original declaration is in sigsegv.cpp
1628     // FIXME: If HAVE_MACH_EXCEPTIONS is set, sigsegv_info_t has a bunch of other
1629     // fields too! Let's hope Screen_fault_handler() doesn't use them...
1630     struct sigsegv_info_t {
1631     sigsegv_address_t addr;
1632     sigsegv_address_t pc;
1633     };
1634    
1635 gbeauche 1.26 static void sigsegv_handler(int sig, siginfo_t *sip, void *scp)
1636 cebix 1.1 {
1637 gbeauche 1.26 machine_regs *r = MACHINE_REGISTERS(scp);
1638 gbeauche 1.5
1639     // Get effective address
1640 gbeauche 1.26 uint32 addr = r->dar();
1641 gbeauche 1.5
1642 gbeauche 1.60 #ifdef SYSTEM_CLOBBERS_R2
1643     // Restore pointer to Thread Local Storage
1644     set_r2(TOC);
1645     #endif
1646     #ifdef SYSTEM_CLOBBERS_R13
1647     // Restore pointer to .sdata section
1648     set_r13(R13);
1649     #endif
1650    
1651 gbeauche 1.5 #if ENABLE_VOSF
1652 gbeauche 1.79 // Handle screen fault
1653     #if SIGSEGV_CHECK_VERSION(1,0,0)
1654     sigsegv_info_t si;
1655     si.addr = (sigsegv_address_t)addr;
1656     si.pc = (sigsegv_address_t)r->pc();
1657     #endif
1658     extern bool Screen_fault_handler(sigsegv_info_t *sip);
1659     if (Screen_fault_handler(&si))
1660 gbeauche 1.5 return;
1661     #endif
1662    
1663 cebix 1.1 num_segv++;
1664    
1665 gbeauche 1.37 // Fault in Mac ROM or RAM or DR Cache?
1666     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));
1667 cebix 1.1 if (mac_fault) {
1668    
1669     // "VM settings" during MacOS 8 installation
1670 gbeauche 1.26 if (r->pc() == ROM_BASE + 0x488160 && r->gpr(20) == 0xf8000000) {
1671     r->pc() += 4;
1672     r->gpr(8) = 0;
1673 cebix 1.1 return;
1674    
1675     // MacOS 8.5 installation
1676 gbeauche 1.26 } else if (r->pc() == ROM_BASE + 0x488140 && r->gpr(16) == 0xf8000000) {
1677     r->pc() += 4;
1678     r->gpr(8) = 0;
1679 cebix 1.1 return;
1680    
1681     // MacOS 8 serial drivers on startup
1682 gbeauche 1.26 } else if (r->pc() == ROM_BASE + 0x48e080 && (r->gpr(8) == 0xf3012002 || r->gpr(8) == 0xf3012000)) {
1683     r->pc() += 4;
1684     r->gpr(8) = 0;
1685 cebix 1.1 return;
1686    
1687     // MacOS 8.1 serial drivers on startup
1688 gbeauche 1.26 } else if (r->pc() == ROM_BASE + 0x48c5e0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1689     r->pc() += 4;
1690 cebix 1.1 return;
1691 gbeauche 1.26 } else if (r->pc() == ROM_BASE + 0x4a10a0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1692     r->pc() += 4;
1693 cebix 1.1 return;
1694 gbeauche 1.37
1695     // MacOS 8.6 serial drivers on startup (with DR Cache and OldWorld ROM)
1696     } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(16) == 0xf3012002 || r->gpr(16) == 0xf3012000)) {
1697     r->pc() += 4;
1698     return;
1699     } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1700     r->pc() += 4;
1701     return;
1702 cebix 1.1 }
1703    
1704 gbeauche 1.5 // Get opcode and divide into fields
1705 gbeauche 1.26 uint32 opcode = *((uint32 *)r->pc());
1706 gbeauche 1.5 uint32 primop = opcode >> 26;
1707     uint32 exop = (opcode >> 1) & 0x3ff;
1708     uint32 ra = (opcode >> 16) & 0x1f;
1709     uint32 rb = (opcode >> 11) & 0x1f;
1710     uint32 rd = (opcode >> 21) & 0x1f;
1711     int32 imm = (int16)(opcode & 0xffff);
1712    
1713 cebix 1.1 // Analyze opcode
1714     enum {
1715     TYPE_UNKNOWN,
1716     TYPE_LOAD,
1717     TYPE_STORE
1718     } transfer_type = TYPE_UNKNOWN;
1719     enum {
1720     SIZE_UNKNOWN,
1721     SIZE_BYTE,
1722     SIZE_HALFWORD,
1723     SIZE_WORD
1724     } transfer_size = SIZE_UNKNOWN;
1725     enum {
1726     MODE_UNKNOWN,
1727     MODE_NORM,
1728     MODE_U,
1729     MODE_X,
1730     MODE_UX
1731     } addr_mode = MODE_UNKNOWN;
1732     switch (primop) {
1733     case 31:
1734     switch (exop) {
1735     case 23: // lwzx
1736     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1737     case 55: // lwzux
1738     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1739     case 87: // lbzx
1740     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1741     case 119: // lbzux
1742     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1743     case 151: // stwx
1744     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1745     case 183: // stwux
1746     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1747     case 215: // stbx
1748     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1749     case 247: // stbux
1750     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1751     case 279: // lhzx
1752     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1753     case 311: // lhzux
1754     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1755     case 343: // lhax
1756     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1757     case 375: // lhaux
1758     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1759     case 407: // sthx
1760     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1761     case 439: // sthux
1762     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1763     }
1764     break;
1765    
1766     case 32: // lwz
1767     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1768     case 33: // lwzu
1769     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
1770     case 34: // lbz
1771     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
1772     case 35: // lbzu
1773     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
1774     case 36: // stw
1775     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1776     case 37: // stwu
1777     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
1778     case 38: // stb
1779     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
1780     case 39: // stbu
1781     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
1782     case 40: // lhz
1783     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1784     case 41: // lhzu
1785     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1786     case 42: // lha
1787     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1788     case 43: // lhau
1789     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1790     case 44: // sth
1791     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1792     case 45: // sthu
1793     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1794 gbeauche 1.23 #if EMULATE_UNALIGNED_LOADSTORE_MULTIPLE
1795     case 46: // lmw
1796 gbeauche 1.27 if ((addr % 4) != 0) {
1797     uint32 ea = addr;
1798 gbeauche 1.26 D(bug("WARNING: unaligned lmw to EA=%08x from IP=%08x\n", ea, r->pc()));
1799 gbeauche 1.23 for (int i = rd; i <= 31; i++) {
1800 gbeauche 1.26 r->gpr(i) = ReadMacInt32(ea);
1801 gbeauche 1.23 ea += 4;
1802     }
1803 gbeauche 1.26 r->pc() += 4;
1804 gbeauche 1.23 goto rti;
1805     }
1806     break;
1807     case 47: // stmw
1808 gbeauche 1.27 if ((addr % 4) != 0) {
1809     uint32 ea = addr;
1810 gbeauche 1.26 D(bug("WARNING: unaligned stmw to EA=%08x from IP=%08x\n", ea, r->pc()));
1811 gbeauche 1.23 for (int i = rd; i <= 31; i++) {
1812 gbeauche 1.26 WriteMacInt32(ea, r->gpr(i));
1813 gbeauche 1.23 ea += 4;
1814     }
1815 gbeauche 1.26 r->pc() += 4;
1816 gbeauche 1.23 goto rti;
1817     }
1818     break;
1819     #endif
1820 cebix 1.1 }
1821    
1822 gbeauche 1.31 // Ignore ROM writes (including to the zero page, which is read-only)
1823     if (transfer_type == TYPE_STORE &&
1824     ((addr >= ROM_BASE && addr < ROM_BASE + ROM_SIZE) ||
1825     (addr >= SheepMem::ZeroPage() && addr < SheepMem::ZeroPage() + SheepMem::PageSize()))) {
1826 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()));
1827 cebix 1.1 if (addr_mode == MODE_U || addr_mode == MODE_UX)
1828 gbeauche 1.26 r->gpr(ra) = addr;
1829     r->pc() += 4;
1830 cebix 1.1 goto rti;
1831     }
1832    
1833     // Ignore illegal memory accesses?
1834     if (PrefsFindBool("ignoresegv")) {
1835     if (addr_mode == MODE_U || addr_mode == MODE_UX)
1836 gbeauche 1.26 r->gpr(ra) = addr;
1837 cebix 1.1 if (transfer_type == TYPE_LOAD)
1838 gbeauche 1.26 r->gpr(rd) = 0;
1839     r->pc() += 4;
1840 cebix 1.1 goto rti;
1841     }
1842    
1843     // In GUI mode, show error alert
1844     if (!PrefsFindBool("nogui")) {
1845     char str[256];
1846     if (transfer_type == TYPE_LOAD || transfer_type == TYPE_STORE)
1847 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));
1848 cebix 1.1 else
1849 gbeauche 1.26 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode);
1850 cebix 1.1 ErrorAlert(str);
1851     QuitEmulator();
1852     return;
1853     }
1854     }
1855    
1856     // For all other errors, jump into debugger (sort of...)
1857 gbeauche 1.23 crash_reason = (sig == SIGBUS) ? "SIGBUS" : "SIGSEGV";
1858 cebix 1.1 if (!ready_for_signals) {
1859 gbeauche 1.23 printf("%s\n");
1860 gbeauche 1.26 printf(" sigcontext %p, machine_regs %p\n", scp, r);
1861 cebix 1.1 printf(
1862     " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
1863     " xer %08lx cr %08lx \n"
1864     " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
1865     " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
1866     " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
1867     " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
1868     " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
1869     " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
1870     " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
1871     " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
1872 gbeauche 1.23 crash_reason,
1873 gbeauche 1.26 r->pc(), r->lr(), r->ctr(), r->msr(),
1874     r->xer(), r->cr(),
1875     r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3),
1876     r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7),
1877     r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11),
1878     r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15),
1879     r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19),
1880     r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23),
1881     r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27),
1882     r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31));
1883 cebix 1.1 exit(1);
1884     QuitEmulator();
1885     return;
1886     } else {
1887     // We crashed. Save registers, tell tick thread and loop forever
1888 gbeauche 1.26 build_sigregs(&sigsegv_regs, r);
1889 cebix 1.1 emul_thread_fatal = true;
1890     for (;;) ;
1891     }
1892     rti:;
1893     }
1894    
1895    
1896     /*
1897     * SIGILL handler
1898     */
1899    
1900 gbeauche 1.26 static void sigill_handler(int sig, siginfo_t *sip, void *scp)
1901 cebix 1.1 {
1902 gbeauche 1.26 machine_regs *r = MACHINE_REGISTERS(scp);
1903 cebix 1.1 char str[256];
1904    
1905 gbeauche 1.60 #ifdef SYSTEM_CLOBBERS_R2
1906     // Restore pointer to Thread Local Storage
1907     set_r2(TOC);
1908     #endif
1909     #ifdef SYSTEM_CLOBBERS_R13
1910     // Restore pointer to .sdata section
1911     set_r13(R13);
1912     #endif
1913    
1914 cebix 1.1 // Fault in Mac ROM or RAM?
1915 gbeauche 1.26 bool mac_fault = (r->pc() >= ROM_BASE) && (r->pc() < (ROM_BASE + ROM_AREA_SIZE)) || (r->pc() >= RAMBase) && (r->pc() < (RAMBase + RAMSize));
1916 cebix 1.1 if (mac_fault) {
1917    
1918     // Get opcode and divide into fields
1919 gbeauche 1.26 uint32 opcode = *((uint32 *)r->pc());
1920 cebix 1.1 uint32 primop = opcode >> 26;
1921     uint32 exop = (opcode >> 1) & 0x3ff;
1922     uint32 ra = (opcode >> 16) & 0x1f;
1923     uint32 rb = (opcode >> 11) & 0x1f;
1924     uint32 rd = (opcode >> 21) & 0x1f;
1925     int32 imm = (int16)(opcode & 0xffff);
1926    
1927     switch (primop) {
1928     case 9: // POWER instructions
1929     case 22:
1930 gbeauche 1.26 power_inst: sprintf(str, GetString(STR_POWER_INSTRUCTION_ERR), r->pc(), r->gpr(1), opcode);
1931 cebix 1.1 ErrorAlert(str);
1932     QuitEmulator();
1933     return;
1934    
1935     case 31:
1936     switch (exop) {
1937     case 83: // mfmsr
1938 gbeauche 1.26 r->gpr(rd) = 0xf072;
1939     r->pc() += 4;
1940 cebix 1.1 goto rti;
1941    
1942     case 210: // mtsr
1943     case 242: // mtsrin
1944     case 306: // tlbie
1945 gbeauche 1.26 r->pc() += 4;
1946 cebix 1.1 goto rti;
1947    
1948     case 339: { // mfspr
1949     int spr = ra | (rb << 5);
1950     switch (spr) {
1951     case 0: // MQ
1952     case 22: // DEC
1953     case 952: // MMCR0
1954     case 953: // PMC1
1955     case 954: // PMC2
1956     case 955: // SIA
1957     case 956: // MMCR1
1958     case 957: // PMC3
1959     case 958: // PMC4
1960     case 959: // SDA
1961 gbeauche 1.26 r->pc() += 4;
1962 cebix 1.1 goto rti;
1963     case 25: // SDR1
1964 gbeauche 1.26 r->gpr(rd) = 0xdead001f;
1965     r->pc() += 4;
1966 cebix 1.1 goto rti;
1967     case 287: // PVR
1968 gbeauche 1.26 r->gpr(rd) = PVR;
1969     r->pc() += 4;
1970 cebix 1.1 goto rti;
1971     }
1972     break;
1973     }
1974    
1975     case 467: { // mtspr
1976     int spr = ra | (rb << 5);
1977     switch (spr) {
1978     case 0: // MQ
1979     case 22: // DEC
1980     case 275: // SPRG3
1981     case 528: // IBAT0U
1982     case 529: // IBAT0L
1983     case 530: // IBAT1U
1984     case 531: // IBAT1L
1985     case 532: // IBAT2U
1986     case 533: // IBAT2L
1987     case 534: // IBAT3U
1988     case 535: // IBAT3L
1989     case 536: // DBAT0U
1990     case 537: // DBAT0L
1991     case 538: // DBAT1U
1992     case 539: // DBAT1L
1993     case 540: // DBAT2U
1994     case 541: // DBAT2L
1995     case 542: // DBAT3U
1996     case 543: // DBAT3L
1997     case 952: // MMCR0
1998     case 953: // PMC1
1999     case 954: // PMC2
2000     case 955: // SIA
2001     case 956: // MMCR1
2002     case 957: // PMC3
2003     case 958: // PMC4
2004     case 959: // SDA
2005 gbeauche 1.26 r->pc() += 4;
2006 cebix 1.1 goto rti;
2007     }
2008     break;
2009     }
2010    
2011     case 29: case 107: case 152: case 153: // POWER instructions
2012     case 184: case 216: case 217: case 248:
2013     case 264: case 277: case 331: case 360:
2014     case 363: case 488: case 531: case 537:
2015     case 541: case 664: case 665: case 696:
2016     case 728: case 729: case 760: case 920:
2017     case 921: case 952:
2018     goto power_inst;
2019     }
2020     }
2021    
2022     // In GUI mode, show error alert
2023     if (!PrefsFindBool("nogui")) {
2024 gbeauche 1.26 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode);
2025 cebix 1.1 ErrorAlert(str);
2026     QuitEmulator();
2027     return;
2028     }
2029     }
2030    
2031     // For all other errors, jump into debugger (sort of...)
2032 gbeauche 1.23 crash_reason = "SIGILL";
2033 cebix 1.1 if (!ready_for_signals) {
2034 gbeauche 1.23 printf("%s\n");
2035 gbeauche 1.26 printf(" sigcontext %p, machine_regs %p\n", scp, r);
2036 cebix 1.1 printf(
2037     " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
2038     " xer %08lx cr %08lx \n"
2039     " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
2040     " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
2041     " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
2042     " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
2043     " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
2044     " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
2045     " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
2046     " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
2047 gbeauche 1.23 crash_reason,
2048 gbeauche 1.26 r->pc(), r->lr(), r->ctr(), r->msr(),
2049     r->xer(), r->cr(),
2050     r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3),
2051     r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7),
2052     r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11),
2053     r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15),
2054     r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19),
2055     r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23),
2056     r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27),
2057     r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31));
2058 cebix 1.1 exit(1);
2059     QuitEmulator();
2060     return;
2061     } else {
2062     // We crashed. Save registers, tell tick thread and loop forever
2063 gbeauche 1.26 build_sigregs(&sigsegv_regs, r);
2064 cebix 1.1 emul_thread_fatal = true;
2065     for (;;) ;
2066     }
2067     rti:;
2068     }
2069     #endif
2070 gbeauche 1.15
2071    
2072     /*
2073     * Helpers to share 32-bit addressable data with MacOS
2074     */
2075    
2076     bool SheepMem::Init(void)
2077     {
2078 gbeauche 1.31 // Size of a native page
2079     page_size = getpagesize();
2080 gbeauche 1.20
2081     // Allocate SheepShaver globals
2082 gbeauche 1.53 proc = base;
2083     if (vm_mac_acquire(base, size) < 0)
2084 gbeauche 1.15 return false;
2085 gbeauche 1.18
2086 gbeauche 1.53 // Allocate page with all bits set to 0, right in the middle
2087     // This is also used to catch undesired overlaps between proc and data areas
2088     zero_page = proc + (size / 2);
2089     Mac_memset(zero_page, 0, page_size);
2090     if (vm_protect(Mac2HostAddr(zero_page), page_size, VM_PAGE_READ) < 0)
2091 gbeauche 1.18 return false;
2092    
2093 gbeauche 1.20 #if EMULATED_PPC
2094     // Allocate alternate stack for PowerPC interrupt routine
2095 gbeauche 1.53 sig_stack = base + size;
2096     if (vm_mac_acquire(sig_stack, SIG_STACK_SIZE) < 0)
2097 gbeauche 1.20 return false;
2098     #endif
2099    
2100 gbeauche 1.53 data = base + size;
2101 gbeauche 1.15 return true;
2102     }
2103    
2104     void SheepMem::Exit(void)
2105     {
2106 gbeauche 1.53 if (data) {
2107 gbeauche 1.20 // Delete SheepShaver globals
2108 gbeauche 1.53 vm_mac_release(base, size);
2109 gbeauche 1.20
2110     #if EMULATED_PPC
2111     // Delete alternate stack for PowerPC interrupt routine
2112 gbeauche 1.53 vm_mac_release(sig_stack, SIG_STACK_SIZE);
2113 gbeauche 1.20 #endif
2114 gbeauche 1.18 }
2115 gbeauche 1.15 }
2116 cebix 1.1
2117    
2118     /*
2119     * Display alert
2120     */
2121    
2122     #ifdef ENABLE_GTK
2123     static void dl_destroyed(void)
2124     {
2125     gtk_main_quit();
2126     }
2127    
2128     static void dl_quit(GtkWidget *dialog)
2129     {
2130     gtk_widget_destroy(dialog);
2131     }
2132    
2133     void display_alert(int title_id, int prefix_id, int button_id, const char *text)
2134     {
2135     char str[256];
2136     sprintf(str, GetString(prefix_id), text);
2137    
2138     GtkWidget *dialog = gtk_dialog_new();
2139     gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
2140     gtk_container_border_width(GTK_CONTAINER(dialog), 5);
2141     gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
2142     gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
2143    
2144     GtkWidget *label = gtk_label_new(str);
2145     gtk_widget_show(label);
2146     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
2147    
2148     GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
2149     gtk_widget_show(button);
2150     gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
2151     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
2152     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2153     gtk_widget_grab_default(button);
2154     gtk_widget_show(dialog);
2155    
2156     gtk_main();
2157     }
2158     #endif
2159    
2160    
2161     /*
2162     * Display error alert
2163     */
2164    
2165     void ErrorAlert(const char *text)
2166     {
2167 gbeauche 1.74 if (gui_connection) {
2168     if (rpc_method_invoke(gui_connection, RPC_METHOD_ERROR_ALERT, RPC_TYPE_STRING, text, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR &&
2169     rpc_method_wait_for_reply(gui_connection, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR)
2170     return;
2171     }
2172 gbeauche 1.42 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
2173 cebix 1.1 if (PrefsFindBool("nogui") || x_display == NULL) {
2174     printf(GetString(STR_SHELL_ERROR_PREFIX), text);
2175     return;
2176     }
2177     VideoQuitFullScreen();
2178     display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
2179     #else
2180     printf(GetString(STR_SHELL_ERROR_PREFIX), text);
2181     #endif
2182     }
2183    
2184    
2185     /*
2186     * Display warning alert
2187     */
2188    
2189     void WarningAlert(const char *text)
2190     {
2191 gbeauche 1.74 if (gui_connection) {
2192     if (rpc_method_invoke(gui_connection, RPC_METHOD_WARNING_ALERT, RPC_TYPE_STRING, text, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR &&
2193     rpc_method_wait_for_reply(gui_connection, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR)
2194     return;
2195     }
2196 gbeauche 1.42 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
2197 cebix 1.1 if (PrefsFindBool("nogui") || x_display == NULL) {
2198     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2199     return;
2200     }
2201     display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
2202     #else
2203     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2204     #endif
2205     }
2206    
2207    
2208     /*
2209     * Display choice alert
2210     */
2211    
2212     bool ChoiceAlert(const char *text, const char *pos, const char *neg)
2213     {
2214     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2215     return false; //!!
2216     }