ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.38
Committed: 2001-07-07T09:07:38Z (23 years ago) by gbeauche
Branch: MAIN
Changes since 1.37: +10 -10 lines
Log Message:
- Try to map memory contiguously with base addresses returned in increasing
  order. No host memory region used for Mac emulation (ScratchMem, RAM, ROM,
  frame buffer) shall be allocated below the RAM space. Actually, MEMBaseDiff
  should be set to the min(above-mentioned address spaces).
  ==> Temporary fix for 64-bit addressing systems (e.g. Linux/ia64)

File Contents

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