ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/main_unix.cpp
Revision: 1.34
Committed: 2001-06-28T21:19:59Z (23 years, 1 month ago) by cebix
Branch: MAIN
Changes since 1.33: +1 -2 lines
Log Message:
video_x.cpp supports resolution switching in windowed mode: the available
resolutions are 512x384, 640x480, 800x600, 1024x768 and 1280x1024 (the prefs
editor has to be updated to reflect this). The resolution selected in the
prefs editor is used as the default, but it can be changed in the Monitors
control panel. So far only tested with direct addressing.

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