ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.77
Committed: 2007-01-13T18:21:30Z (17 years, 7 months ago) by gbeauche
Branch: MAIN
Changes since 1.76: +3 -25 lines
Log Message:
Remove the 33-bit addressing hack as it's overly complex for not much gain.
Rather, use an address override prefix (0x67) though Intel Core optimization
reference guide says to avoid LCP prefixes. In practise, impact on performance
is measurably marginal on e.g. Speedometer tests.

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