ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.64
Committed: 2004-11-15T23:27:43Z (19 years, 10 months ago) by gbeauche
Branch: MAIN
Changes since 1.63: +3 -0 lines
Log Message:
Backport copy-paste of 'TEXT' from SheepShaver

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * main_unix.cpp - Startup code for Unix
3     *
4 cebix 1.58 * Basilisk II (C) 1997-2004 Christian Bauer
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     #include "sysdeps.h"
22    
23     #include <stdio.h>
24     #include <stdlib.h>
25     #include <signal.h>
26 cebix 1.12 #include <errno.h>
27 gbeauche 1.59
28     #ifdef USE_SDL
29     # include <SDL.h>
30     #endif
31    
32     #ifndef USE_SDL_VIDEO
33     # include <X11/Xlib.h>
34     #endif
35 cebix 1.12
36     #ifdef HAVE_PTHREADS
37     # include <pthread.h>
38     #endif
39    
40 cebix 1.27 #if REAL_ADDRESSING || DIRECT_ADDRESSING
41 cebix 1.12 # include <sys/mman.h>
42     #endif
43    
44     #if !EMULATED_68K && defined(__NetBSD__)
45     # include <m68k/sync_icache.h>
46     # include <m68k/frame.h>
47     # include <sys/param.h>
48     # include <sys/sysctl.h>
49     struct sigstate {
50     int ss_flags;
51     struct frame ss_frame;
52     struct fpframe ss_fpstate;
53     };
54     # define SS_FPSTATE 0x02
55     # define SS_USERREGS 0x04
56     #endif
57    
58     #ifdef ENABLE_GTK
59     # include <gtk/gtk.h>
60 cebix 1.28 # include <gdk/gdk.h>
61 cebix 1.43 # ifdef HAVE_GNOMEUI
62     # include <gnome.h>
63     # endif
64 cebix 1.12 #endif
65    
66     #ifdef ENABLE_XF86_DGA
67     # include <X11/Xutil.h>
68     # include <X11/extensions/xf86dga.h>
69     #endif
70 cebix 1.1
71 cebix 1.48 #include <string>
72     using std::string;
73    
74 cebix 1.1 #include "cpu_emulation.h"
75     #include "sys.h"
76 cebix 1.3 #include "rom_patches.h"
77 cebix 1.1 #include "xpram.h"
78     #include "timer.h"
79     #include "video.h"
80 cebix 1.12 #include "emul_op.h"
81 cebix 1.1 #include "prefs.h"
82     #include "prefs_editor.h"
83     #include "macos_util.h"
84     #include "user_strings.h"
85     #include "version.h"
86     #include "main.h"
87 gbeauche 1.33 #include "vm_alloc.h"
88 gbeauche 1.46 #include "sigsegv.h"
89 cebix 1.1
90 gbeauche 1.50 #if USE_JIT
91 gbeauche 1.57 extern void flush_icache_range(uint32 start, uint32 size); // from compemu_support.cpp
92 gbeauche 1.50 #endif
93    
94 cebix 1.12 #ifdef ENABLE_MON
95     # include "mon.h"
96     #endif
97    
98 cebix 1.13 #define DEBUG 0
99 cebix 1.1 #include "debug.h"
100    
101    
102 cebix 1.12 // Constants
103     const char ROM_FILE_NAME[] = "ROM";
104 gbeauche 1.51 #if !EMULATED_68K
105 cebix 1.12 const int SIG_STACK_SIZE = SIGSTKSZ; // Size of signal stack
106 gbeauche 1.51 #endif
107 cebix 1.12 const int SCRATCH_MEM_SIZE = 0x10000; // Size of scratch memory area
108 cebix 1.1
109 cebix 1.4
110 cebix 1.12 #if !EMULATED_68K
111     // RAM and ROM pointers
112     uint32 RAMBaseMac; // RAM base (Mac address space)
113     uint8 *RAMBaseHost; // RAM base (host address space)
114     uint32 RAMSize; // Size of RAM
115     uint32 ROMBaseMac; // ROM base (Mac address space)
116     uint8 *ROMBaseHost; // ROM base (host address space)
117     uint32 ROMSize; // Size of ROM
118 cebix 1.9 #endif
119    
120 cebix 1.1
121     // CPU and FPU type, addressing mode
122     int CPUType;
123     bool CPUIs68060;
124     int FPUType;
125     bool TwentyFourBitAddressing;
126 gbeauche 1.62 bool ThirtyThreeBitAddressing = false;
127 cebix 1.1
128    
129     // Global variables
130 gbeauche 1.59 #ifndef USE_SDL_VIDEO
131     extern char *x_display_name; // X11 display name
132     extern Display *x_display; // X11 display handle
133 gbeauche 1.64 #ifdef X11_LOCK_TYPE
134     X11_LOCK_TYPE x_display_lock = X11_LOCK_INIT; // X11 display lock
135     #endif
136 gbeauche 1.59 #endif
137 cebix 1.1
138 cebix 1.41 static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes
139 cebix 1.12
140     #ifdef HAVE_PTHREADS
141 gbeauche 1.51 #if !EMULATED_68K
142 cebix 1.12 static pthread_t emul_thread; // Handle of MacOS emulation thread (main thread)
143 gbeauche 1.51 #endif
144 cebix 1.12
145 cebix 1.1 static bool xpram_thread_active = false; // Flag: XPRAM watchdog installed
146     static volatile bool xpram_thread_cancel = false; // Flag: Cancel XPRAM thread
147     static pthread_t xpram_thread; // XPRAM watchdog
148    
149     static bool tick_thread_active = false; // Flag: 60Hz thread installed
150     static volatile bool tick_thread_cancel = false; // Flag: Cancel 60Hz thread
151     static pthread_t tick_thread; // 60Hz thread
152     static pthread_attr_t tick_thread_attr; // 60Hz thread attributes
153    
154     static pthread_mutex_t intflag_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect InterruptFlags
155 cebix 1.37 #define LOCK_INTFLAGS pthread_mutex_lock(&intflag_lock)
156     #define UNLOCK_INTFLAGS pthread_mutex_unlock(&intflag_lock)
157    
158     #else
159    
160     #define LOCK_INTFLAGS
161     #define UNLOCK_INTFLAGS
162    
163 cebix 1.12 #endif
164    
165     #if !EMULATED_68K
166     #define SIG_IRQ SIGUSR1
167     static struct sigaction sigirq_sa; // Virtual 68k interrupt signal
168     static struct sigaction sigill_sa; // Illegal instruction
169     static void *sig_stack = NULL; // Stack for signal handlers
170     uint16 EmulatedSR; // Emulated bits of SR (supervisor bit and interrupt mask)
171 gbeauche 1.20 #endif
172    
173     #if USE_SCRATCHMEM_SUBTERFUGE
174 cebix 1.22 uint8 *ScratchMem = NULL; // Scratch memory for Mac ROM writes
175 cebix 1.12 #endif
176    
177 gbeauche 1.51 #if !defined(HAVE_PTHREADS)
178 cebix 1.12 static struct sigaction timer_sa; // sigaction used for timer
179 cebix 1.1
180     #if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
181     #define SIG_TIMER SIGRTMIN
182 cebix 1.12 static timer_t timer; // 60Hz timer
183 cebix 1.1 #endif
184 gbeauche 1.51 #endif // !HAVE_PTHREADS
185 cebix 1.1
186 cebix 1.12 #ifdef ENABLE_MON
187     static struct sigaction sigint_sa; // sigaction for SIGINT handler
188 cebix 1.4 static void sigint_handler(...);
189 cebix 1.15 #endif
190    
191     #if REAL_ADDRESSING
192     static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
193 cebix 1.22 #endif
194    
195 cebix 1.1
196     // Prototypes
197     static void *xpram_func(void *arg);
198     static void *tick_func(void *arg);
199     static void one_tick(...);
200 cebix 1.12 #if !EMULATED_68K
201     static void sigirq_handler(int sig, int code, struct sigcontext *scp);
202     static void sigill_handler(int sig, int code, struct sigcontext *scp);
203     extern "C" void EmulOpTrampoline(void);
204     #endif
205 cebix 1.1
206    
207     /*
208     * Ersatz functions
209     */
210    
211     extern "C" {
212    
213     #ifndef HAVE_STRDUP
214     char *strdup(const char *s)
215     {
216     char *n = (char *)malloc(strlen(s) + 1);
217     strcpy(n, s);
218     return n;
219     }
220     #endif
221    
222     }
223    
224    
225     /*
226 gbeauche 1.62 * Map memory that can be accessed from the Mac side
227     */
228    
229     void *vm_acquire_mac(size_t size)
230     {
231     void *m = vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_33BIT);
232     if (m == NULL) {
233     ThirtyThreeBitAddressing = false;
234     m = vm_acquire(size);
235     }
236     return m;
237     }
238    
239    
240     /*
241 gbeauche 1.54 * SIGSEGV handler
242     */
243    
244     static sigsegv_return_t sigsegv_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
245     {
246     #if ENABLE_VOSF
247     // Handle screen fault
248     extern bool Screen_fault_handler(sigsegv_address_t, sigsegv_address_t);
249     if (Screen_fault_handler(fault_address, fault_instruction))
250     return SIGSEGV_RETURN_SUCCESS;
251     #endif
252    
253     #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
254     // Ignore writes to ROM
255     if (((uintptr)fault_address - (uintptr)ROMBaseHost) < ROMSize)
256     return SIGSEGV_RETURN_SKIP_INSTRUCTION;
257    
258     // Ignore all other faults, if requested
259     if (PrefsFindBool("ignoresegv"))
260     return SIGSEGV_RETURN_SKIP_INSTRUCTION;
261     #endif
262    
263     return SIGSEGV_RETURN_FAILURE;
264     }
265    
266     /*
267 gbeauche 1.47 * Dump state when everything went wrong after a SEGV
268     */
269    
270     static void sigsegv_dump_state(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
271     {
272 gbeauche 1.50 fprintf(stderr, "Caught SIGSEGV at address %p", fault_address);
273 gbeauche 1.47 if (fault_instruction != SIGSEGV_INVALID_PC)
274     fprintf(stderr, " [IP=%p]", fault_instruction);
275     fprintf(stderr, "\n");
276     #if EMULATED_68K
277     uaecptr nextpc;
278     extern void m68k_dumpstate(uaecptr *nextpc);
279     m68k_dumpstate(&nextpc);
280     #endif
281 gbeauche 1.50 #if USE_JIT && JIT_DEBUG
282     extern void compiler_dumpstate(void);
283     compiler_dumpstate();
284     #endif
285 gbeauche 1.47 VideoQuitFullScreen();
286     #ifdef ENABLE_MON
287     char *arg[4] = {"mon", "-m", "-r", NULL};
288     mon(3, arg);
289     QuitEmulator();
290     #endif
291     }
292    
293    
294     /*
295 cebix 1.1 * Main program
296     */
297    
298 cebix 1.32 static void usage(const char *prg_name)
299     {
300 cebix 1.48 printf(
301     "Usage: %s [OPTION...]\n"
302     "\nUnix options:\n"
303     " --config FILE\n read/write configuration from/to FILE\n"
304     " --display STRING\n X display to use\n"
305     " --break ADDRESS\n set ROM breakpoint\n"
306     " --rominfo\n dump ROM information\n", prg_name
307     );
308     LoadPrefs(); // read the prefs file so PrefsPrintUsage() will print the correct default values
309 cebix 1.32 PrefsPrintUsage();
310     exit(0);
311     }
312    
313 cebix 1.1 int main(int argc, char **argv)
314     {
315 cebix 1.12 char str[256];
316    
317 cebix 1.1 // Initialize variables
318     RAMBaseHost = NULL;
319     ROMBaseHost = NULL;
320     srand(time(NULL));
321     tzset();
322    
323     // Print some info
324     printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
325     printf(" %s\n", GetString(STR_ABOUT_TEXT2));
326    
327 cebix 1.48 // Parse command line arguments
328     for (int i=1; i<argc; i++) {
329     if (strcmp(argv[i], "--help") == 0) {
330     usage(argv[0]);
331 gbeauche 1.59 #ifndef USE_SDL_VIDEO
332 cebix 1.48 } else if (strcmp(argv[i], "--display") == 0) {
333     i++; // don't remove the argument, gtk_init() needs it too
334     if (i < argc)
335     x_display_name = strdup(argv[i]);
336 gbeauche 1.59 #endif
337 cebix 1.48 } else if (strcmp(argv[i], "--break") == 0) {
338     argv[i++] = NULL;
339     if (i < argc) {
340     ROMBreakpoint = strtol(argv[i], NULL, 0);
341     argv[i] = NULL;
342     }
343     } else if (strcmp(argv[i], "--config") == 0) {
344     argv[i++] = NULL;
345     if (i < argc) {
346     extern string UserPrefsPath; // from prefs_unix.cpp
347     UserPrefsPath = argv[i];
348     argv[i] = NULL;
349     }
350     } else if (strcmp(argv[i], "--rominfo") == 0) {
351     argv[i] = NULL;
352     PrintROMInfo = true;
353     }
354     }
355    
356     // Remove processed arguments
357     for (int i=1; i<argc; i++) {
358     int k;
359     for (k=i; k<argc; k++)
360     if (argv[k] != NULL)
361     break;
362     if (k > i) {
363     k -= i;
364     for (int j=i+k; j<argc; j++)
365     argv[j-k] = argv[j];
366     argc -= k;
367     }
368     }
369    
370 cebix 1.28 #ifdef ENABLE_GTK
371 cebix 1.43 #ifdef HAVE_GNOMEUI
372     // Init GNOME/GTK
373     char version[16];
374     sprintf(version, "%d.%d", VERSION_MAJOR, VERSION_MINOR);
375     gnome_init("Basilisk II", version, argc, argv);
376     #else
377 cebix 1.28 // Init GTK
378     gtk_set_locale();
379     gtk_init(&argc, &argv);
380 cebix 1.43 #endif
381 cebix 1.28 #endif
382    
383 cebix 1.32 // Read preferences
384     PrefsInit(argc, argv);
385    
386 cebix 1.48 // Any command line arguments left?
387 cebix 1.1 for (int i=1; i<argc; i++) {
388 cebix 1.48 if (argv[i][0] == '-') {
389 cebix 1.32 fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
390     usage(argv[0]);
391 cebix 1.28 }
392 cebix 1.1 }
393    
394 gbeauche 1.59 #ifndef USE_SDL_VIDEO
395 cebix 1.1 // Open display
396     x_display = XOpenDisplay(x_display_name);
397     if (x_display == NULL) {
398     char str[256];
399     sprintf(str, GetString(STR_NO_XSERVER_ERR), XDisplayName(x_display_name));
400     ErrorAlert(str);
401     QuitEmulator();
402     }
403    
404 cebix 1.12 #if defined(ENABLE_XF86_DGA) && !defined(ENABLE_MON)
405 cebix 1.1 // Fork out, so we can return from fullscreen mode when things get ugly
406 cebix 1.2 XF86DGAForkApp(DefaultScreen(x_display));
407 cebix 1.1 #endif
408 gbeauche 1.59 #endif
409    
410     #ifdef USE_SDL
411     // Initialize SDL system
412     int sdl_flags = 0;
413     #ifdef USE_SDL_VIDEO
414     sdl_flags |= SDL_INIT_VIDEO;
415     #endif
416 gbeauche 1.61 #ifdef USE_SDL_AUDIO
417     sdl_flags |= SDL_INIT_AUDIO;
418     #endif
419 gbeauche 1.59 assert(sdl_flags != 0);
420     if (SDL_Init(sdl_flags) == -1) {
421     char str[256];
422     sprintf(str, "Could not initialize SDL: %s.\n", SDL_GetError());
423     ErrorAlert(str);
424     QuitEmulator();
425     }
426 gbeauche 1.60 atexit(SDL_Quit);
427 gbeauche 1.59 #endif
428 cebix 1.1
429     // Init system routines
430     SysInit();
431    
432     // Show preferences editor
433     if (!PrefsFindBool("nogui"))
434     if (!PrefsEditor())
435     QuitEmulator();
436 gbeauche 1.46
437 gbeauche 1.54 // Install the handler for SIGSEGV
438 gbeauche 1.55 if (!sigsegv_install_handler(sigsegv_handler)) {
439     sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGSEGV", strerror(errno));
440     ErrorAlert(str);
441     QuitEmulator();
442     }
443 gbeauche 1.54
444 gbeauche 1.47 // Register dump state function when we got mad after a segfault
445     sigsegv_set_dump_state(sigsegv_dump_state);
446 cebix 1.1
447 cebix 1.9 // Read RAM size
448 cebix 1.1 RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary
449     if (RAMSize < 1024*1024) {
450     WarningAlert(GetString(STR_SMALL_RAM_WARN));
451     RAMSize = 1024*1024;
452     }
453 cebix 1.9
454 gbeauche 1.20 #if REAL_ADDRESSING || DIRECT_ADDRESSING
455 gbeauche 1.33 RAMSize = RAMSize & -getpagesize(); // Round down to page boundary
456 gbeauche 1.20 #endif
457 gbeauche 1.33
458     // Initialize VM system
459     vm_init();
460 gbeauche 1.20
461 cebix 1.12 #if REAL_ADDRESSING
462 gbeauche 1.33 // Flag: RAM and ROM are contigously allocated from address 0
463     bool memory_mapped_from_zero = false;
464    
465     // Under Solaris/SPARC and NetBSD/m68k, Basilisk II is known to crash
466     // when trying to map a too big chunk of memory starting at address 0
467 gbeauche 1.56 #if defined(OS_solaris) || defined(OS_netbsd) || defined(PAGEZERO_HACK)
468 gbeauche 1.33 const bool can_map_all_memory = false;
469 gbeauche 1.20 #else
470 gbeauche 1.33 const bool can_map_all_memory = true;
471 gbeauche 1.20 #endif
472 gbeauche 1.33
473     // Try to allocate all memory from 0x0000, if it is not known to crash
474     if (can_map_all_memory && (vm_acquire_fixed(0, RAMSize + 0x100000) == 0)) {
475 gbeauche 1.20 D(bug("Could allocate RAM and ROM from 0x0000\n"));
476     memory_mapped_from_zero = true;
477     }
478 gbeauche 1.33
479 gbeauche 1.56 #ifndef PAGEZERO_HACK
480 gbeauche 1.33 // Otherwise, just create the Low Memory area (0x0000..0x2000)
481     else if (vm_acquire_fixed(0, 0x2000) == 0) {
482 gbeauche 1.20 D(bug("Could allocate the Low Memory globals\n"));
483     lm_area_mapped = true;
484     }
485 gbeauche 1.33
486     // Exit on failure
487 gbeauche 1.20 else {
488 cebix 1.12 sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno));
489     ErrorAlert(str);
490     QuitEmulator();
491     }
492     #endif
493 gbeauche 1.56 #endif /* REAL_ADDRESSING */
494 cebix 1.12
495 cebix 1.9 // Create areas for Mac RAM and ROM
496 gbeauche 1.20 #if REAL_ADDRESSING
497     if (memory_mapped_from_zero) {
498     RAMBaseHost = (uint8 *)0;
499 gbeauche 1.33 ROMBaseHost = RAMBaseHost + RAMSize;
500 gbeauche 1.20 }
501     else
502     #endif
503     {
504 gbeauche 1.62 #ifdef USE_33BIT_ADDRESSING
505     // Speculatively enables 33-bit addressing
506     ThirtyThreeBitAddressing = true;
507     #endif
508     RAMBaseHost = (uint8 *)vm_acquire_mac(RAMSize);
509     ROMBaseHost = (uint8 *)vm_acquire_mac(0x100000);
510 gbeauche 1.33 if (RAMBaseHost == VM_MAP_FAILED || ROMBaseHost == VM_MAP_FAILED) {
511 cebix 1.36 ErrorAlert(STR_NO_MEM_ERR);
512 gbeauche 1.20 QuitEmulator();
513     }
514     }
515 gbeauche 1.38
516     #if USE_SCRATCHMEM_SUBTERFUGE
517     // Allocate scratch memory
518     ScratchMem = (uint8 *)vm_acquire(SCRATCH_MEM_SIZE);
519     if (ScratchMem == VM_MAP_FAILED) {
520     ErrorAlert(STR_NO_MEM_ERR);
521     QuitEmulator();
522     }
523     ScratchMem += SCRATCH_MEM_SIZE/2; // ScratchMem points to middle of block
524     #endif
525 cebix 1.22
526 gbeauche 1.20 #if DIRECT_ADDRESSING
527 gbeauche 1.33 // RAMBaseMac shall always be zero
528     MEMBaseDiff = (uintptr)RAMBaseHost;
529 gbeauche 1.20 RAMBaseMac = 0;
530 gbeauche 1.33 ROMBaseMac = Host2MacAddr(ROMBaseHost);
531 gbeauche 1.20 #endif
532 gbeauche 1.33 #if REAL_ADDRESSING
533 cebix 1.12 RAMBaseMac = (uint32)RAMBaseHost;
534     ROMBaseMac = (uint32)ROMBaseHost;
535     #endif
536     D(bug("Mac RAM starts at %p (%08x)\n", RAMBaseHost, RAMBaseMac));
537     D(bug("Mac ROM starts at %p (%08x)\n", ROMBaseHost, ROMBaseMac));
538 gbeauche 1.20
539 cebix 1.1 // Get rom file path from preferences
540     const char *rom_path = PrefsFindString("rom");
541    
542     // Load Mac ROM
543     int rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY);
544     if (rom_fd < 0) {
545 cebix 1.36 ErrorAlert(STR_NO_ROM_FILE_ERR);
546 cebix 1.1 QuitEmulator();
547     }
548     printf(GetString(STR_READING_ROM_FILE));
549     ROMSize = lseek(rom_fd, 0, SEEK_END);
550     if (ROMSize != 64*1024 && ROMSize != 128*1024 && ROMSize != 256*1024 && ROMSize != 512*1024 && ROMSize != 1024*1024) {
551 cebix 1.36 ErrorAlert(STR_ROM_SIZE_ERR);
552 cebix 1.1 close(rom_fd);
553     QuitEmulator();
554     }
555     lseek(rom_fd, 0, SEEK_SET);
556     if (read(rom_fd, ROMBaseHost, ROMSize) != (ssize_t)ROMSize) {
557 cebix 1.36 ErrorAlert(STR_ROM_FILE_READ_ERR);
558 cebix 1.1 close(rom_fd);
559     QuitEmulator();
560     }
561    
562 cebix 1.12 #if !EMULATED_68K
563     // Get CPU model
564     int mib[2] = {CTL_HW, HW_MODEL};
565     char *model;
566     size_t model_len;
567     sysctl(mib, 2, NULL, &model_len, NULL, 0);
568     model = (char *)malloc(model_len);
569     sysctl(mib, 2, model, &model_len, NULL, 0);
570     D(bug("Model: %s\n", model));
571    
572     // Set CPU and FPU type
573     CPUIs68060 = false;
574     if (strstr(model, "020"))
575     CPUType = 2;
576     else if (strstr(model, "030"))
577     CPUType = 3;
578     else if (strstr(model, "040"))
579     CPUType = 4;
580     else if (strstr(model, "060")) {
581     CPUType = 4;
582     CPUIs68060 = true;
583     } else {
584     printf("WARNING: Cannot detect CPU type, assuming 68020\n");
585     CPUType = 2;
586     }
587 cebix 1.24 FPUType = 1; // NetBSD has an FPU emulation, so the FPU ought to be available at all times
588 cebix 1.12 TwentyFourBitAddressing = false;
589     #endif
590    
591 cebix 1.3 // Initialize everything
592     if (!InitAll())
593 cebix 1.1 QuitEmulator();
594 cebix 1.12 D(bug("Initialization complete\n"));
595    
596 gbeauche 1.51 #if !EMULATED_68K
597     // (Virtual) supervisor mode, disable interrupts
598     EmulatedSR = 0x2700;
599    
600 cebix 1.12 #ifdef HAVE_PTHREADS
601     // Get handle of main thread
602     emul_thread = pthread_self();
603     #endif
604    
605     // Create and install stack for signal handlers
606     sig_stack = malloc(SIG_STACK_SIZE);
607     D(bug("Signal stack at %p\n", sig_stack));
608     if (sig_stack == NULL) {
609 cebix 1.36 ErrorAlert(STR_NOT_ENOUGH_MEMORY_ERR);
610 cebix 1.12 QuitEmulator();
611     }
612     stack_t new_stack;
613     new_stack.ss_sp = sig_stack;
614     new_stack.ss_flags = 0;
615     new_stack.ss_size = SIG_STACK_SIZE;
616     if (sigaltstack(&new_stack, NULL) < 0) {
617     sprintf(str, GetString(STR_SIGALTSTACK_ERR), strerror(errno));
618     ErrorAlert(str);
619     QuitEmulator();
620     }
621    
622     // Install SIGILL handler for emulating privileged instructions and
623     // executing A-Trap and EMUL_OP opcodes
624     sigemptyset(&sigill_sa.sa_mask); // Block virtual 68k interrupts during SIGILL handling
625     sigaddset(&sigill_sa.sa_mask, SIG_IRQ);
626     sigaddset(&sigill_sa.sa_mask, SIGALRM);
627     sigill_sa.sa_handler = (void (*)(int))sigill_handler;
628     sigill_sa.sa_flags = SA_ONSTACK;
629     if (sigaction(SIGILL, &sigill_sa, NULL) < 0) {
630     sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGILL", strerror(errno));
631     ErrorAlert(str);
632     QuitEmulator();
633     }
634    
635     // Install virtual 68k interrupt signal handler
636     sigemptyset(&sigirq_sa.sa_mask);
637     sigaddset(&sigirq_sa.sa_mask, SIGALRM);
638     sigirq_sa.sa_handler = (void (*)(int))sigirq_handler;
639     sigirq_sa.sa_flags = SA_ONSTACK | SA_RESTART;
640     if (sigaction(SIG_IRQ, &sigirq_sa, NULL) < 0) {
641     sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIG_IRQ", strerror(errno));
642     ErrorAlert(str);
643     QuitEmulator();
644     }
645     #endif
646 cebix 1.1
647 cebix 1.12 #ifdef ENABLE_MON
648     // Setup SIGINT handler to enter mon
649     sigemptyset(&sigint_sa.sa_mask);
650 cebix 1.21 sigint_sa.sa_handler = (void (*)(int))sigint_handler;
651 cebix 1.12 sigint_sa.sa_flags = 0;
652     sigaction(SIGINT, &sigint_sa, NULL);
653     #endif
654 cebix 1.1
655 cebix 1.39 #if defined(HAVE_PTHREADS)
656    
657     // POSIX threads available, start 60Hz thread
658 cebix 1.44 Set_pthread_attr(&tick_thread_attr, 0);
659 cebix 1.39 tick_thread_active = (pthread_create(&tick_thread, &tick_thread_attr, tick_func, NULL) == 0);
660     if (!tick_thread_active) {
661     sprintf(str, GetString(STR_TICK_THREAD_ERR), strerror(errno));
662     ErrorAlert(str);
663     QuitEmulator();
664     }
665     D(bug("60Hz thread started\n"));
666    
667     #elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
668 cebix 1.12
669     // POSIX.4 timers and real-time signals available, start 60Hz timer
670 cebix 1.1 sigemptyset(&timer_sa.sa_mask);
671 cebix 1.19 timer_sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))one_tick;
672 cebix 1.1 timer_sa.sa_flags = SA_SIGINFO | SA_RESTART;
673     if (sigaction(SIG_TIMER, &timer_sa, NULL) < 0) {
674 cebix 1.12 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIG_TIMER", strerror(errno));
675     ErrorAlert(str);
676 cebix 1.1 QuitEmulator();
677     }
678     struct sigevent timer_event;
679     timer_event.sigev_notify = SIGEV_SIGNAL;
680     timer_event.sigev_signo = SIG_TIMER;
681     if (timer_create(CLOCK_REALTIME, &timer_event, &timer) < 0) {
682 cebix 1.12 sprintf(str, GetString(STR_TIMER_CREATE_ERR), strerror(errno));
683     ErrorAlert(str);
684 cebix 1.1 QuitEmulator();
685     }
686     struct itimerspec req;
687     req.it_value.tv_sec = 0;
688     req.it_value.tv_nsec = 16625000;
689     req.it_interval.tv_sec = 0;
690     req.it_interval.tv_nsec = 16625000;
691 cebix 1.10 if (timer_settime(timer, 0, &req, NULL) < 0) {
692 cebix 1.12 sprintf(str, GetString(STR_TIMER_SETTIME_ERR), strerror(errno));
693     ErrorAlert(str);
694 cebix 1.1 QuitEmulator();
695     }
696 cebix 1.12 D(bug("60Hz timer started\n"));
697 cebix 1.1
698 cebix 1.12 #else
699    
700     // Start 60Hz timer
701     sigemptyset(&timer_sa.sa_mask); // Block virtual 68k interrupts during SIGARLM handling
702 cebix 1.53 #if !EMULATED_68K
703 cebix 1.12 sigaddset(&timer_sa.sa_mask, SIG_IRQ);
704 cebix 1.53 #endif
705 cebix 1.12 timer_sa.sa_handler = one_tick;
706     timer_sa.sa_flags = SA_ONSTACK | SA_RESTART;
707     if (sigaction(SIGALRM, &timer_sa, NULL) < 0) {
708     sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGALRM", strerror(errno));
709     ErrorAlert(str);
710 cebix 1.1 QuitEmulator();
711     }
712 cebix 1.12 struct itimerval req;
713     req.it_interval.tv_sec = req.it_value.tv_sec = 0;
714     req.it_interval.tv_usec = req.it_value.tv_usec = 16625;
715     setitimer(ITIMER_REAL, &req, NULL);
716    
717 cebix 1.1 #endif
718    
719 cebix 1.12 #ifdef HAVE_PTHREADS
720     // Start XPRAM watchdog thread
721 cebix 1.41 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
722 cebix 1.12 xpram_thread_active = (pthread_create(&xpram_thread, NULL, xpram_func, NULL) == 0);
723     D(bug("XPRAM thread started\n"));
724 cebix 1.4 #endif
725    
726 cebix 1.1 // Start 68k and jump to ROM boot routine
727 cebix 1.12 D(bug("Starting emulation...\n"));
728 cebix 1.1 Start680x0();
729    
730     QuitEmulator();
731     return 0;
732     }
733    
734    
735     /*
736     * Quit emulator
737     */
738    
739     void QuitEmulator(void)
740     {
741 cebix 1.12 D(bug("QuitEmulator\n"));
742    
743     #if EMULATED_68K
744 cebix 1.1 // Exit 680x0 emulation
745     Exit680x0();
746 cebix 1.12 #endif
747 cebix 1.1
748 cebix 1.39 #if defined(HAVE_PTHREADS)
749 cebix 1.1 // Stop 60Hz thread
750     if (tick_thread_active) {
751     tick_thread_cancel = true;
752     #ifdef HAVE_PTHREAD_CANCEL
753     pthread_cancel(tick_thread);
754     #endif
755     pthread_join(tick_thread, NULL);
756     }
757 cebix 1.39 #elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
758     // Stop 60Hz timer
759     timer_delete(timer);
760 cebix 1.12 #else
761     struct itimerval req;
762     req.it_interval.tv_sec = req.it_value.tv_sec = 0;
763     req.it_interval.tv_usec = req.it_value.tv_usec = 0;
764     setitimer(ITIMER_REAL, &req, NULL);
765 cebix 1.1 #endif
766    
767 cebix 1.12 #ifdef HAVE_PTHREADS
768 cebix 1.1 // Stop XPRAM watchdog thread
769     if (xpram_thread_active) {
770     xpram_thread_cancel = true;
771     #ifdef HAVE_PTHREAD_CANCEL
772     pthread_cancel(xpram_thread);
773     #endif
774     pthread_join(xpram_thread, NULL);
775     }
776 cebix 1.12 #endif
777 cebix 1.1
778 cebix 1.3 // Deinitialize everything
779     ExitAll();
780 cebix 1.1
781 cebix 1.22 // Free ROM/RAM areas
782 gbeauche 1.33 if (RAMBaseHost != VM_MAP_FAILED) {
783     vm_release(RAMBaseHost, RAMSize);
784 cebix 1.22 RAMBaseHost = NULL;
785 gbeauche 1.20 }
786 gbeauche 1.33 if (ROMBaseHost != VM_MAP_FAILED) {
787     vm_release(ROMBaseHost, 0x100000);
788 cebix 1.17 ROMBaseHost = NULL;
789     }
790 cebix 1.1
791 cebix 1.22 #if USE_SCRATCHMEM_SUBTERFUGE
792 cebix 1.12 // Delete scratch memory area
793 gbeauche 1.33 if (ScratchMem != (uint8 *)VM_MAP_FAILED) {
794     vm_release((void *)(ScratchMem - SCRATCH_MEM_SIZE/2), SCRATCH_MEM_SIZE);
795 cebix 1.17 ScratchMem = NULL;
796     }
797 cebix 1.12 #endif
798    
799     #if REAL_ADDRESSING
800     // Delete Low Memory area
801     if (lm_area_mapped)
802 gbeauche 1.33 vm_release(0, 0x2000);
803 cebix 1.12 #endif
804 gbeauche 1.33
805     // Exit VM wrappers
806     vm_exit();
807 cebix 1.12
808 cebix 1.1 // Exit system routines
809     SysExit();
810    
811     // Exit preferences
812     PrefsExit();
813    
814     // Close X11 server connection
815 gbeauche 1.60 #ifndef USE_SDL_VIDEO
816 cebix 1.1 if (x_display)
817     XCloseDisplay(x_display);
818 gbeauche 1.59 #endif
819 cebix 1.1
820     exit(0);
821     }
822    
823    
824     /*
825     * Code was patched, flush caches if neccessary (i.e. when using a real 680x0
826     * or a dynamically recompiling emulator)
827     */
828    
829     void FlushCodeCache(void *start, uint32 size)
830     {
831 gbeauche 1.50 #if USE_JIT
832     if (UseJIT)
833 gbeauche 1.57 flush_icache_range((uintptr)start, size);
834 gbeauche 1.50 #endif
835 cebix 1.12 #if !EMULATED_68K && defined(__NetBSD__)
836     m68k_sync_icache(start, size);
837     #endif
838 cebix 1.4 }
839    
840    
841     /*
842     * SIGINT handler, enters mon
843     */
844    
845 cebix 1.12 #ifdef ENABLE_MON
846 cebix 1.4 static void sigint_handler(...)
847     {
848 cebix 1.12 #if EMULATED_68K
849 cebix 1.8 uaecptr nextpc;
850 cebix 1.12 extern void m68k_dumpstate(uaecptr *nextpc);
851 cebix 1.8 m68k_dumpstate(&nextpc);
852 cebix 1.34 #endif
853 cebix 1.37 VideoQuitFullScreen();
854 cebix 1.21 char *arg[4] = {"mon", "-m", "-r", NULL};
855     mon(3, arg);
856 cebix 1.4 QuitEmulator();
857 cebix 1.1 }
858     #endif
859    
860    
861 cebix 1.44 #ifdef HAVE_PTHREADS
862     /*
863 cebix 1.45 * Pthread configuration
864 cebix 1.44 */
865 cebix 1.45
866     void Set_pthread_attr(pthread_attr_t *attr, int priority)
867 cebix 1.44 {
868     pthread_attr_init(attr);
869     #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
870     // Some of these only work for superuser
871     if (geteuid() == 0) {
872     pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
873     pthread_attr_setschedpolicy(attr, SCHED_FIFO);
874     struct sched_param fifo_param;
875     fifo_param.sched_priority = ((sched_get_priority_min(SCHED_FIFO) +
876     sched_get_priority_max(SCHED_FIFO)) / 2 +
877     priority);
878     pthread_attr_setschedparam(attr, &fifo_param);
879     }
880     if (pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) != 0) {
881     #ifdef PTHREAD_SCOPE_BOUND_NP
882     // If system scope is not available (eg. we're not running
883     // with CAP_SCHED_MGT capability on an SGI box), try bound
884     // scope. It exposes pthread scheduling to the kernel,
885     // without setting realtime priority.
886     pthread_attr_setscope(attr, PTHREAD_SCOPE_BOUND_NP);
887     #endif
888     }
889     #endif
890     }
891     #endif // HAVE_PTHREADS
892    
893    
894 cebix 1.1 /*
895 cebix 1.37 * Mutexes
896     */
897    
898     #ifdef HAVE_PTHREADS
899    
900     struct B2_mutex {
901 cebix 1.44 B2_mutex() {
902     pthread_mutexattr_t attr;
903     pthread_mutexattr_init(&attr);
904     // Initialize the mutex for priority inheritance --
905     // required for accurate timing.
906 gbeauche 1.63 #if defined(HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL) && !defined(__CYGWIN__)
907 cebix 1.44 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
908     #endif
909     #if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL)
910     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
911     #endif
912 gbeauche 1.49 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
913 cebix 1.44 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
914 gbeauche 1.49 #endif
915 cebix 1.44 pthread_mutex_init(&m, &attr);
916     pthread_mutexattr_destroy(&attr);
917     }
918 gbeauche 1.51 ~B2_mutex() {
919     pthread_mutex_trylock(&m); // Make sure it's locked before
920     pthread_mutex_unlock(&m); // unlocking it.
921     pthread_mutex_destroy(&m);
922     }
923 cebix 1.37 pthread_mutex_t m;
924     };
925    
926     B2_mutex *B2_create_mutex(void)
927     {
928     return new B2_mutex;
929     }
930    
931     void B2_lock_mutex(B2_mutex *mutex)
932     {
933     pthread_mutex_lock(&mutex->m);
934     }
935    
936     void B2_unlock_mutex(B2_mutex *mutex)
937     {
938     pthread_mutex_unlock(&mutex->m);
939     }
940    
941     void B2_delete_mutex(B2_mutex *mutex)
942     {
943     delete mutex;
944     }
945    
946     #else
947    
948     struct B2_mutex {
949     int dummy;
950     };
951    
952     B2_mutex *B2_create_mutex(void)
953     {
954     return new B2_mutex;
955     }
956    
957     void B2_lock_mutex(B2_mutex *mutex)
958     {
959     }
960    
961     void B2_unlock_mutex(B2_mutex *mutex)
962     {
963     }
964    
965     void B2_delete_mutex(B2_mutex *mutex)
966     {
967     delete mutex;
968     }
969    
970     #endif
971    
972    
973     /*
974 cebix 1.1 * Interrupt flags (must be handled atomically!)
975     */
976    
977     uint32 InterruptFlags = 0;
978    
979 cebix 1.12 #if EMULATED_68K
980 cebix 1.1 void SetInterruptFlag(uint32 flag)
981     {
982 cebix 1.37 LOCK_INTFLAGS;
983 cebix 1.1 InterruptFlags |= flag;
984 cebix 1.37 UNLOCK_INTFLAGS;
985 cebix 1.1 }
986    
987     void ClearInterruptFlag(uint32 flag)
988     {
989 cebix 1.37 LOCK_INTFLAGS;
990 cebix 1.1 InterruptFlags &= ~flag;
991 cebix 1.37 UNLOCK_INTFLAGS;
992 cebix 1.12 }
993     #endif
994    
995     #if !EMULATED_68K
996     void TriggerInterrupt(void)
997     {
998     #if defined(HAVE_PTHREADS)
999     pthread_kill(emul_thread, SIG_IRQ);
1000     #else
1001     raise(SIG_IRQ);
1002     #endif
1003 cebix 1.22 }
1004    
1005     void TriggerNMI(void)
1006     {
1007     // not yet supported
1008 cebix 1.12 }
1009     #endif
1010    
1011    
1012     /*
1013     * XPRAM watchdog thread (saves XPRAM every minute)
1014     */
1015    
1016     static void xpram_watchdog(void)
1017     {
1018 cebix 1.41 if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
1019     memcpy(last_xpram, XPRAM, XPRAM_SIZE);
1020 cebix 1.12 SaveXPRAM();
1021     }
1022     }
1023    
1024     #ifdef HAVE_PTHREADS
1025     static void *xpram_func(void *arg)
1026     {
1027     while (!xpram_thread_cancel) {
1028 cebix 1.16 for (int i=0; i<60 && !xpram_thread_cancel; i++)
1029 cebix 1.29 Delay_usec(999999); // Only wait 1 second so we quit promptly when xpram_thread_cancel becomes true
1030 cebix 1.12 xpram_watchdog();
1031     }
1032     return NULL;
1033 cebix 1.1 }
1034 cebix 1.12 #endif
1035 cebix 1.1
1036    
1037     /*
1038     * 60Hz thread (really 60.15Hz)
1039     */
1040    
1041 cebix 1.12 static void one_second(void)
1042     {
1043     // Pseudo Mac 1Hz interrupt, update local time
1044     WriteMacInt32(0x20c, TimerDateTime());
1045    
1046 cebix 1.18 SetInterruptFlag(INTFLAG_1HZ);
1047 cebix 1.14 TriggerInterrupt();
1048    
1049 cebix 1.12 #ifndef HAVE_PTHREADS
1050     static int second_counter = 0;
1051     if (++second_counter > 60) {
1052     second_counter = 0;
1053     xpram_watchdog();
1054     }
1055     #endif
1056     }
1057    
1058 cebix 1.1 static void one_tick(...)
1059     {
1060     static int tick_counter = 0;
1061     if (++tick_counter > 60) {
1062     tick_counter = 0;
1063 cebix 1.12 one_second();
1064 cebix 1.1 }
1065    
1066 gbeauche 1.59 #if !defined(HAVE_PTHREADS) && !defined(USE_SDL_VIDEO)
1067 cebix 1.40 // No threads available, perform video refresh and networking from here
1068 cebix 1.12 VideoRefresh();
1069 cebix 1.40 SetInterruptFlag(INTFLAG_ETHER);
1070 cebix 1.12 #endif
1071    
1072 cebix 1.1 // Trigger 60Hz interrupt
1073     if (ROMVersion != ROM_VERSION_CLASSIC || HasMacStarted()) {
1074     SetInterruptFlag(INTFLAG_60HZ);
1075     TriggerInterrupt();
1076     }
1077     }
1078    
1079 cebix 1.12 #ifdef HAVE_PTHREADS
1080 cebix 1.1 static void *tick_func(void *arg)
1081     {
1082 cebix 1.39 uint64 start = GetTicks_usec();
1083     int64 ticks = 0;
1084 cebix 1.16 uint64 next = GetTicks_usec();
1085 cebix 1.1 while (!tick_thread_cancel) {
1086 cebix 1.16 one_tick();
1087     next += 16625;
1088     int64 delay = next - GetTicks_usec();
1089     if (delay > 0)
1090     Delay_usec(delay);
1091     else if (delay < -16625)
1092     next = GetTicks_usec();
1093 cebix 1.39 ticks++;
1094 cebix 1.16 }
1095 cebix 1.39 uint64 end = GetTicks_usec();
1096     D(bug("%Ld ticks in %Ld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
1097 cebix 1.16 return NULL;
1098     }
1099     #endif
1100 cebix 1.12
1101    
1102     #if !EMULATED_68K
1103     /*
1104     * Virtual 68k interrupt handler
1105     */
1106    
1107     static void sigirq_handler(int sig, int code, struct sigcontext *scp)
1108     {
1109     // Interrupts disabled? Then do nothing
1110     if (EmulatedSR & 0x0700)
1111     return;
1112    
1113     struct sigstate *state = (struct sigstate *)scp->sc_ap;
1114     M68kRegisters *regs = (M68kRegisters *)&state->ss_frame;
1115    
1116     // Set up interrupt frame on stack
1117     uint32 a7 = regs->a[7];
1118     a7 -= 2;
1119     WriteMacInt16(a7, 0x64);
1120     a7 -= 4;
1121     WriteMacInt32(a7, scp->sc_pc);
1122     a7 -= 2;
1123     WriteMacInt16(a7, scp->sc_ps | EmulatedSR);
1124     scp->sc_sp = regs->a[7] = a7;
1125    
1126     // Set interrupt level
1127     EmulatedSR |= 0x2100;
1128    
1129     // Jump to MacOS interrupt handler on return
1130     scp->sc_pc = ReadMacInt32(0x64);
1131     }
1132 cebix 1.1
1133    
1134     /*
1135 cebix 1.12 * SIGILL handler, for emulation of privileged instructions and executing
1136     * A-Trap and EMUL_OP opcodes
1137 cebix 1.1 */
1138    
1139 cebix 1.12 static void sigill_handler(int sig, int code, struct sigcontext *scp)
1140 cebix 1.1 {
1141 cebix 1.12 struct sigstate *state = (struct sigstate *)scp->sc_ap;
1142     uint16 *pc = (uint16 *)scp->sc_pc;
1143     uint16 opcode = *pc;
1144     M68kRegisters *regs = (M68kRegisters *)&state->ss_frame;
1145    
1146     #define INC_PC(n) scp->sc_pc += (n)
1147    
1148     #define GET_SR (scp->sc_ps | EmulatedSR)
1149    
1150     #define STORE_SR(v) \
1151     scp->sc_ps = (v) & 0xff; \
1152 cebix 1.24 EmulatedSR = (v) & 0xe700; \
1153 cebix 1.12 if (((v) & 0x0700) == 0 && InterruptFlags) \
1154     TriggerInterrupt();
1155    
1156     //printf("opcode %04x at %p, sr %04x, emul_sr %04x\n", opcode, pc, scp->sc_ps, EmulatedSR);
1157    
1158     if ((opcode & 0xf000) == 0xa000) {
1159    
1160     // A-Line instruction, set up A-Line trap frame on stack
1161     uint32 a7 = regs->a[7];
1162     a7 -= 2;
1163     WriteMacInt16(a7, 0x28);
1164     a7 -= 4;
1165     WriteMacInt32(a7, (uint32)pc);
1166     a7 -= 2;
1167     WriteMacInt16(a7, GET_SR);
1168     scp->sc_sp = regs->a[7] = a7;
1169    
1170     // Jump to MacOS A-Line handler on return
1171     scp->sc_pc = ReadMacInt32(0x28);
1172    
1173     } else if ((opcode & 0xff00) == 0x7100) {
1174    
1175     // Extended opcode, push registers on user stack
1176     uint32 a7 = regs->a[7];
1177     a7 -= 4;
1178     WriteMacInt32(a7, (uint32)pc);
1179     a7 -= 2;
1180     WriteMacInt16(a7, scp->sc_ps);
1181     for (int i=7; i>=0; i--) {
1182     a7 -= 4;
1183     WriteMacInt32(a7, regs->a[i]);
1184     }
1185     for (int i=7; i>=0; i--) {
1186     a7 -= 4;
1187     WriteMacInt32(a7, regs->d[i]);
1188     }
1189     scp->sc_sp = regs->a[7] = a7;
1190    
1191     // Jump to EmulOp trampoline code on return
1192     scp->sc_pc = (uint32)EmulOpTrampoline;
1193    
1194     } else switch (opcode) { // Emulate privileged instructions
1195    
1196     case 0x40e7: // move sr,-(sp)
1197     regs->a[7] -= 2;
1198     WriteMacInt16(regs->a[7], GET_SR);
1199     scp->sc_sp = regs->a[7];
1200     INC_PC(2);
1201     break;
1202    
1203     case 0x46df: { // move (sp)+,sr
1204     uint16 sr = ReadMacInt16(regs->a[7]);
1205     STORE_SR(sr);
1206     regs->a[7] += 2;
1207     scp->sc_sp = regs->a[7];
1208     INC_PC(2);
1209     break;
1210     }
1211    
1212     case 0x007c: { // ori #xxxx,sr
1213     uint16 sr = GET_SR | pc[1];
1214     scp->sc_ps = sr & 0xff; // oring bits into the sr can't enable interrupts, so we don't need to call STORE_SR
1215 cebix 1.24 EmulatedSR = sr & 0xe700;
1216 cebix 1.12 INC_PC(4);
1217     break;
1218     }
1219    
1220     case 0x027c: { // andi #xxxx,sr
1221     uint16 sr = GET_SR & pc[1];
1222     STORE_SR(sr);
1223     INC_PC(4);
1224     break;
1225     }
1226    
1227     case 0x46fc: // move #xxxx,sr
1228     STORE_SR(pc[1]);
1229     INC_PC(4);
1230     break;
1231    
1232     case 0x46ef: { // move (xxxx,sp),sr
1233     uint16 sr = ReadMacInt16(regs->a[7] + (int32)(int16)pc[1]);
1234     STORE_SR(sr);
1235     INC_PC(4);
1236     break;
1237     }
1238    
1239     case 0x46d8: // move (a0)+,sr
1240     case 0x46d9: { // move (a1)+,sr
1241     uint16 sr = ReadMacInt16(regs->a[opcode & 7]);
1242     STORE_SR(sr);
1243     regs->a[opcode & 7] += 2;
1244     INC_PC(2);
1245     break;
1246     }
1247 cebix 1.1
1248 cebix 1.12 case 0x40f8: // move sr,xxxx.w
1249     WriteMacInt16(pc[1], GET_SR);
1250     INC_PC(4);
1251     break;
1252    
1253     case 0x40d0: // move sr,(a0)
1254     case 0x40d1: // move sr,(a1)
1255     case 0x40d2: // move sr,(a2)
1256     case 0x40d3: // move sr,(a3)
1257     case 0x40d4: // move sr,(a4)
1258     case 0x40d5: // move sr,(a5)
1259     case 0x40d6: // move sr,(a6)
1260     case 0x40d7: // move sr,(sp)
1261     WriteMacInt16(regs->a[opcode & 7], GET_SR);
1262     INC_PC(2);
1263     break;
1264    
1265     case 0x40c0: // move sr,d0
1266     case 0x40c1: // move sr,d1
1267     case 0x40c2: // move sr,d2
1268     case 0x40c3: // move sr,d3
1269     case 0x40c4: // move sr,d4
1270     case 0x40c5: // move sr,d5
1271     case 0x40c6: // move sr,d6
1272     case 0x40c7: // move sr,d7
1273     regs->d[opcode & 7] = GET_SR;
1274     INC_PC(2);
1275     break;
1276    
1277     case 0x46c0: // move d0,sr
1278     case 0x46c1: // move d1,sr
1279     case 0x46c2: // move d2,sr
1280     case 0x46c3: // move d3,sr
1281     case 0x46c4: // move d4,sr
1282     case 0x46c5: // move d5,sr
1283     case 0x46c6: // move d6,sr
1284     case 0x46c7: { // move d7,sr
1285     uint16 sr = regs->d[opcode & 7];
1286     STORE_SR(sr);
1287     INC_PC(2);
1288     break;
1289 cebix 1.1 }
1290 cebix 1.12
1291     case 0xf327: // fsave -(sp)
1292 cebix 1.35 regs->a[7] -= 4;
1293     WriteMacInt32(regs->a[7], 0x41000000); // Idle frame
1294 cebix 1.24 scp->sc_sp = regs->a[7];
1295     INC_PC(2);
1296     break;
1297 cebix 1.12
1298     case 0xf35f: // frestore (sp)+
1299 cebix 1.35 regs->a[7] += 4;
1300 cebix 1.24 scp->sc_sp = regs->a[7];
1301     INC_PC(2);
1302     break;
1303 cebix 1.12
1304 cebix 1.24 case 0x4e73: { // rte
1305 cebix 1.12 uint32 a7 = regs->a[7];
1306     uint16 sr = ReadMacInt16(a7);
1307     a7 += 2;
1308     scp->sc_ps = sr & 0xff;
1309 cebix 1.24 EmulatedSR = sr & 0xe700;
1310 cebix 1.12 scp->sc_pc = ReadMacInt32(a7);
1311 cebix 1.24 a7 += 4;
1312     uint16 format = ReadMacInt16(a7) >> 12;
1313     a7 += 2;
1314     static const int frame_adj[16] = {
1315     0, 0, 4, 4, 8, 0, 0, 52, 50, 12, 24, 84, 16, 0, 0, 0
1316     };
1317     scp->sc_sp = regs->a[7] = a7 + frame_adj[format];
1318 cebix 1.12 break;
1319 cebix 1.1 }
1320 cebix 1.12
1321     case 0x4e7a: // movec cr,x
1322     switch (pc[1]) {
1323     case 0x0002: // movec cacr,d0
1324     regs->d[0] = 0x3111;
1325     break;
1326     case 0x1002: // movec cacr,d1
1327     regs->d[1] = 0x3111;
1328     break;
1329     case 0x0003: // movec tc,d0
1330 cebix 1.24 case 0x0004: // movec itt0,d0
1331     case 0x0005: // movec itt1,d0
1332     case 0x0006: // movec dtt0,d0
1333     case 0x0007: // movec dtt1,d0
1334     case 0x0806: // movec urp,d0
1335     case 0x0807: // movec srp,d0
1336 cebix 1.12 regs->d[0] = 0;
1337     break;
1338 cebix 1.24 case 0x1000: // movec sfc,d1
1339     case 0x1001: // movec dfc,d1
1340 cebix 1.12 case 0x1003: // movec tc,d1
1341 cebix 1.24 case 0x1801: // movec vbr,d1
1342 cebix 1.12 regs->d[1] = 0;
1343     break;
1344 cebix 1.24 case 0x8801: // movec vbr,a0
1345     regs->a[0] = 0;
1346     break;
1347     case 0x9801: // movec vbr,a1
1348     regs->a[1] = 0;
1349     break;
1350 cebix 1.12 default:
1351     goto ill;
1352     }
1353     INC_PC(4);
1354     break;
1355    
1356     case 0x4e7b: // movec x,cr
1357     switch (pc[1]) {
1358 cebix 1.24 case 0x1000: // movec d1,sfc
1359     case 0x1001: // movec d1,dfc
1360 cebix 1.12 case 0x0801: // movec d0,vbr
1361 cebix 1.24 case 0x1801: // movec d1,vbr
1362 cebix 1.12 break;
1363     case 0x0002: // movec d0,cacr
1364     case 0x1002: // movec d1,cacr
1365     FlushCodeCache(NULL, 0);
1366     break;
1367     default:
1368     goto ill;
1369     }
1370     INC_PC(4);
1371     break;
1372    
1373     case 0xf478: // cpusha dc
1374     case 0xf4f8: // cpusha dc/ic
1375     FlushCodeCache(NULL, 0);
1376     INC_PC(2);
1377     break;
1378    
1379     default:
1380     ill: printf("SIGILL num %d, code %d\n", sig, code);
1381     printf(" context %p:\n", scp);
1382     printf(" onstack %08x\n", scp->sc_onstack);
1383     printf(" sp %08x\n", scp->sc_sp);
1384     printf(" fp %08x\n", scp->sc_fp);
1385     printf(" pc %08x\n", scp->sc_pc);
1386     printf(" opcode %04x\n", opcode);
1387     printf(" sr %08x\n", scp->sc_ps);
1388     printf(" state %p:\n", state);
1389     printf(" flags %d\n", state->ss_flags);
1390     for (int i=0; i<8; i++)
1391     printf(" d%d %08x\n", i, state->ss_frame.f_regs[i]);
1392     for (int i=0; i<8; i++)
1393     printf(" a%d %08x\n", i, state->ss_frame.f_regs[i+8]);
1394    
1395 cebix 1.37 VideoQuitFullScreen();
1396 cebix 1.12 #ifdef ENABLE_MON
1397 cebix 1.21 char *arg[4] = {"mon", "-m", "-r", NULL};
1398     mon(3, arg);
1399 cebix 1.12 #endif
1400     QuitEmulator();
1401     break;
1402 cebix 1.1 }
1403     }
1404 cebix 1.12 #endif
1405 cebix 1.1
1406    
1407     /*
1408     * Display alert
1409     */
1410    
1411 cebix 1.12 #ifdef ENABLE_GTK
1412 cebix 1.1 static void dl_destroyed(void)
1413     {
1414     gtk_main_quit();
1415     }
1416    
1417     static void dl_quit(GtkWidget *dialog)
1418     {
1419     gtk_widget_destroy(dialog);
1420     }
1421    
1422     void display_alert(int title_id, int prefix_id, int button_id, const char *text)
1423     {
1424     char str[256];
1425     sprintf(str, GetString(prefix_id), text);
1426    
1427     GtkWidget *dialog = gtk_dialog_new();
1428     gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
1429     gtk_container_border_width(GTK_CONTAINER(dialog), 5);
1430     gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
1431     gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
1432    
1433     GtkWidget *label = gtk_label_new(str);
1434     gtk_widget_show(label);
1435     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
1436    
1437     GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
1438     gtk_widget_show(button);
1439     gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
1440     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
1441     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1442     gtk_widget_grab_default(button);
1443     gtk_widget_show(dialog);
1444    
1445     gtk_main();
1446     }
1447     #endif
1448    
1449    
1450     /*
1451     * Display error alert
1452     */
1453    
1454     void ErrorAlert(const char *text)
1455     {
1456 gbeauche 1.59 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
1457 cebix 1.1 if (PrefsFindBool("nogui") || x_display == NULL) {
1458     printf(GetString(STR_SHELL_ERROR_PREFIX), text);
1459     return;
1460     }
1461     VideoQuitFullScreen();
1462     display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
1463     #else
1464     printf(GetString(STR_SHELL_ERROR_PREFIX), text);
1465     #endif
1466     }
1467    
1468    
1469     /*
1470     * Display warning alert
1471     */
1472    
1473     void WarningAlert(const char *text)
1474     {
1475 gbeauche 1.59 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
1476 cebix 1.1 if (PrefsFindBool("nogui") || x_display == NULL) {
1477     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1478     return;
1479     }
1480     display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
1481     #else
1482     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1483     #endif
1484     }
1485    
1486    
1487     /*
1488     * Display choice alert
1489     */
1490    
1491     bool ChoiceAlert(const char *text, const char *pos, const char *neg)
1492     {
1493     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
1494     return false; //!!
1495     }