ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.47
Committed: 2002-05-16T15:48:06Z (22 years, 2 months ago) by gbeauche
Branch: MAIN
Changes since 1.46: +27 -0 lines
Log Message:
- video_vosh.h (Screen_fault_handler): Move unrecoverable fault case to...
- main_unix.cpp (sigsegv_dump_state): ... Here.
- sigsegv.h (sigsegv_fault_handler_t): Rename from sigsegv_handler_t.
- sigsegv.h (sigsegv_state_dumper_t): New.

File Contents

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