ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.67
Committed: 2005-06-11T06:43:24Z (19 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.66: +77 -7 lines
Log Message:
Much improved responsiveness on NetBSD systems.

On those systems, it's really hard to get high resolution timings and the
system oftens fails to honour a timeout in less than 20 ms. The idea here
is to have an average m68k instruction count (countdown quantum) that
triggers real interrupt checks. The quantum is calibrated every 10 ticks
and has a 1000 Hz resolution on average.

File Contents

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