--- BasiliskII/src/Unix/main_unix.cpp 1999/10/03 14:16:25 1.1.1.1 +++ BasiliskII/src/Unix/main_unix.cpp 2006/02/27 07:24:58 1.72 @@ -1,7 +1,7 @@ /* * main_unix.cpp - Startup code for Unix * - * Basilisk II (C) 1997-1999 Christian Bauer + * Basilisk II (C) 1997-2005 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,49 +22,100 @@ #include #include -#include #include +#include + +#ifdef USE_SDL +# include +#endif + +#ifndef USE_SDL_VIDEO +# include +#endif + +#ifdef HAVE_PTHREADS +# include +#endif + +#if REAL_ADDRESSING || DIRECT_ADDRESSING +# include +#endif + +#if !EMULATED_68K && defined(__NetBSD__) +# include +# include +# include +# include +struct sigstate { + int ss_flags; + struct frame ss_frame; + struct fpframe ss_fpstate; +}; +# define SS_FPSTATE 0x02 +# define SS_USERREGS 0x04 +#endif + +#ifdef ENABLE_GTK +# include +# include +# ifdef HAVE_GNOMEUI +# include +# endif +#endif + +#ifdef ENABLE_XF86_DGA +# include +# include +#endif + +#include +using std::string; #include "cpu_emulation.h" #include "sys.h" +#include "rom_patches.h" #include "xpram.h" #include "timer.h" -#include "sony.h" -#include "disk.h" -#include "cdrom.h" -#include "scsi.h" -#include "audio.h" #include "video.h" -#include "serial.h" -#include "ether.h" -#include "clip.h" -#include "rom_patches.h" +#include "emul_op.h" #include "prefs.h" #include "prefs_editor.h" #include "macos_util.h" #include "user_strings.h" #include "version.h" #include "main.h" +#include "vm_alloc.h" +#include "sigsegv.h" -#define DEBUG 1 -#include "debug.h" - - -#include - -#if ENABLE_GTK -#include +#if USE_JIT +extern void flush_icache_range(uint32 start, uint32 size); // from compemu_support.cpp #endif -#if ENABLE_DGA -#include -#include -#include +#ifdef ENABLE_MON +# include "mon.h" #endif +#define DEBUG 0 +#include "debug.h" + // Constants const char ROM_FILE_NAME[] = "ROM"; +#if !EMULATED_68K +const int SIG_STACK_SIZE = SIGSTKSZ; // Size of signal stack +#endif +const int SCRATCH_MEM_SIZE = 0x10000; // Size of scratch memory area + + +#if !EMULATED_68K +// RAM and ROM pointers +uint32 RAMBaseMac; // RAM base (Mac address space) +uint8 *RAMBaseHost; // RAM base (host address space) +uint32 RAMSize; // Size of RAM +uint32 ROMBaseMac; // ROM base (Mac address space) +uint8 *ROMBaseHost; // ROM base (host address space) +uint32 ROMSize; // Size of ROM +#endif // CPU and FPU type, addressing mode @@ -72,11 +123,24 @@ int CPUType; bool CPUIs68060; int FPUType; bool TwentyFourBitAddressing; +bool ThirtyThreeBitAddressing = false; // Global variables -static char *x_display_name = NULL; // X11 display name -Display *x_display = NULL; // X11 display handle +#ifndef USE_SDL_VIDEO +extern char *x_display_name; // X11 display name +extern Display *x_display; // X11 display handle +#ifdef X11_LOCK_TYPE +X11_LOCK_TYPE x_display_lock = X11_LOCK_INIT; // X11 display lock +#endif +#endif + +static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes + +#ifdef HAVE_PTHREADS +#if !EMULATED_68K +static pthread_t emul_thread; // Handle of MacOS emulation thread (main thread) +#endif static bool xpram_thread_active = false; // Flag: XPRAM watchdog installed static volatile bool xpram_thread_cancel = false; // Flag: Cancel XPRAM thread @@ -88,11 +152,44 @@ static pthread_t tick_thread; // 60 static pthread_attr_t tick_thread_attr; // 60Hz thread attributes static pthread_mutex_t intflag_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect InterruptFlags +#define LOCK_INTFLAGS pthread_mutex_lock(&intflag_lock) +#define UNLOCK_INTFLAGS pthread_mutex_unlock(&intflag_lock) + +#else + +#define LOCK_INTFLAGS +#define UNLOCK_INTFLAGS + +#endif + +#if !EMULATED_68K +#define SIG_IRQ SIGUSR1 +static struct sigaction sigirq_sa; // Virtual 68k interrupt signal +static struct sigaction sigill_sa; // Illegal instruction +static void *sig_stack = NULL; // Stack for signal handlers +uint16 EmulatedSR; // Emulated bits of SR (supervisor bit and interrupt mask) +#endif + +#if USE_SCRATCHMEM_SUBTERFUGE +uint8 *ScratchMem = NULL; // Scratch memory for Mac ROM writes +#endif + +#if !defined(HAVE_PTHREADS) +static struct sigaction timer_sa; // sigaction used for timer #if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) #define SIG_TIMER SIGRTMIN -static struct sigaction timer_sa; // sigaction used for timer -static timer_t timer; // 60Hz timer +static timer_t timer; // 60Hz timer +#endif +#endif // !HAVE_PTHREADS + +#ifdef ENABLE_MON +static struct sigaction sigint_sa; // sigaction for SIGINT handler +static void sigint_handler(...); +#endif + +#if REAL_ADDRESSING +static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped #endif @@ -100,6 +197,11 @@ static timer_t timer; // 60Hz tim static void *xpram_func(void *arg); static void *tick_func(void *arg); static void one_tick(...); +#if !EMULATED_68K +static void sigirq_handler(int sig, int code, struct sigcontext *scp); +static void sigill_handler(int sig, int code, struct sigcontext *scp); +extern "C" void EmulOpTrampoline(void); +#endif /* @@ -121,11 +223,176 @@ char *strdup(const char *s) /* + * Helpers to map memory that can be accessed from the Mac side + */ + +// NOTE: VM_MAP_33BIT is only used when compiling a 64-bit JIT on specific platforms +void *vm_acquire_mac(size_t size) +{ + void *m = vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_33BIT); +#ifdef USE_33BIT_ADDRESSING + if (m == VM_MAP_FAILED) { + printf("WARNING: Cannot acquire memory in 33-bit address space (%s)\n", strerror(errno)); + ThirtyThreeBitAddressing = false; + m = vm_acquire(size); + } +#endif + return m; +} + +static int vm_acquire_mac_fixed(void *addr, size_t size) +{ + int ret = vm_acquire_fixed(addr, size, VM_MAP_DEFAULT | VM_MAP_33BIT); +#ifdef USE_33BIT_ADDRESSING + if (ret < 0) { + printf("WARNING: Cannot acquire fixed memory in 33-bit address space (%s)\n", strerror(errno)); + ThirtyThreeBitAddressing = false; + ret = vm_acquire_fixed(addr, size); + } +#endif + return ret; +} + + +/* + * SIGSEGV handler + */ + +static sigsegv_return_t sigsegv_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction) +{ +#if ENABLE_VOSF + // Handle screen fault + extern bool Screen_fault_handler(sigsegv_address_t, sigsegv_address_t); + if (Screen_fault_handler(fault_address, fault_instruction)) + return SIGSEGV_RETURN_SUCCESS; +#endif + +#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION + // Ignore writes to ROM + if (((uintptr)fault_address - (uintptr)ROMBaseHost) < ROMSize) + return SIGSEGV_RETURN_SKIP_INSTRUCTION; + + // Ignore all other faults, if requested + if (PrefsFindBool("ignoresegv")) + return SIGSEGV_RETURN_SKIP_INSTRUCTION; +#endif + + return SIGSEGV_RETURN_FAILURE; +} + +/* + * Dump state when everything went wrong after a SEGV + */ + +static void sigsegv_dump_state(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction) +{ + fprintf(stderr, "Caught SIGSEGV at address %p", fault_address); + if (fault_instruction != SIGSEGV_INVALID_PC) + fprintf(stderr, " [IP=%p]", fault_instruction); + fprintf(stderr, "\n"); +#if EMULATED_68K + uaecptr nextpc; + extern void m68k_dumpstate(uaecptr *nextpc); + m68k_dumpstate(&nextpc); +#endif +#if USE_JIT && JIT_DEBUG + extern void compiler_dumpstate(void); + compiler_dumpstate(); +#endif + VideoQuitFullScreen(); +#ifdef ENABLE_MON + char *arg[4] = {"mon", "-m", "-r", NULL}; + mon(3, arg); +#endif + QuitEmulator(); +} + + +/* + * Update virtual clock and trigger interrupts if necessary + */ + +#ifdef USE_CPU_EMUL_SERVICES +static uint64 n_check_ticks = 0; +static uint64 emulated_ticks_start = 0; +static uint64 emulated_ticks_count = 0; +static int64 emulated_ticks_current = 0; +static int32 emulated_ticks_quantum = 1000; +int32 emulated_ticks = emulated_ticks_quantum; + +void cpu_do_check_ticks(void) +{ +#if DEBUG + n_check_ticks++; +#endif + + uint64 now; + static uint64 next = 0; + if (next == 0) + next = emulated_ticks_start = GetTicks_usec(); + + // Update total instructions count + if (emulated_ticks <= 0) { + emulated_ticks_current += (emulated_ticks_quantum - emulated_ticks); + // XXX: can you really have a machine fast enough to overflow + // a 63-bit m68k instruction counter within 16 ms? + if (emulated_ticks_current < 0) { + printf("WARNING: Overflowed 63-bit m68k instruction counter in less than 16 ms!\n"); + goto recalibrate_quantum; + } + } + + // Check for interrupt opportunity + now = GetTicks_usec(); + if (next < now) { + one_tick(); + do { + next += 16625; + } while (next < now); + emulated_ticks_count++; + + // Recalibrate 1000 Hz quantum every 10 ticks + static uint64 last = 0; + if (last == 0) + last = now; + else if (now - last > 166250) { + recalibrate_quantum: + emulated_ticks_quantum = ((uint64)emulated_ticks_current * 1000) / (now - last); + emulated_ticks_current = 0; + last = now; + } + } + + // Update countdown + if (emulated_ticks <= 0) + emulated_ticks += emulated_ticks_quantum; +} +#endif + + +/* * Main program */ +static void usage(const char *prg_name) +{ + printf( + "Usage: %s [OPTION...]\n" + "\nUnix options:\n" + " --config FILE\n read/write configuration from/to FILE\n" + " --display STRING\n X display to use\n" + " --break ADDRESS\n set ROM breakpoint\n" + " --rominfo\n dump ROM information\n", prg_name + ); + LoadPrefs(); // read the prefs file so PrefsPrintUsage() will print the correct default values + PrefsPrintUsage(); + exit(0); +} + int main(int argc, char **argv) { + char str[256]; + // Initialize variables RAMBaseHost = NULL; ROMBaseHost = NULL; @@ -136,12 +403,74 @@ int main(int argc, char **argv) printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR); printf(" %s\n", GetString(STR_ABOUT_TEXT2)); - // Parse arguments + // Parse command line arguments + for (int i=1; i i) { + k -= i; + for (int j=i+k; j 1023*1024*1024) // Cap to 1023MB (APD crashes at 1GB) + RAMSize = 1023*1024*1024; + +#if REAL_ADDRESSING || DIRECT_ADDRESSING + RAMSize = RAMSize & -getpagesize(); // Round down to page boundary +#endif + + // Initialize VM system + vm_init(); + +#ifdef USE_33BIT_ADDRESSING + // Speculatively enables 33-bit addressing + ThirtyThreeBitAddressing = true; +#endif + +#if REAL_ADDRESSING + // Flag: RAM and ROM are contigously allocated from address 0 + bool memory_mapped_from_zero = false; + + // Make sure to map RAM & ROM at address 0 only on platforms that + // supports linker scripts to relocate the Basilisk II executable + // above 0x70000000 +#if HAVE_LINKER_SCRIPT + const bool can_map_all_memory = true; +#else + const bool can_map_all_memory = false; +#endif + + // Try to allocate all memory from 0x0000, if it is not known to crash + if (can_map_all_memory && (vm_acquire_mac_fixed(0, RAMSize + 0x100000) == 0)) { + D(bug("Could allocate RAM and ROM from 0x0000\n")); + memory_mapped_from_zero = true; + } + +#ifndef PAGEZERO_HACK + // Otherwise, just create the Low Memory area (0x0000..0x2000) + else if (vm_acquire_mac_fixed(0, 0x2000) == 0) { + D(bug("Could allocate the Low Memory globals\n")); + lm_area_mapped = true; + } + + // Exit on failure + else { + sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno)); + ErrorAlert(str); + QuitEmulator(); + } +#endif +#endif /* REAL_ADDRESSING */ + + // Create areas for Mac RAM and ROM +#if REAL_ADDRESSING + if (memory_mapped_from_zero) { + RAMBaseHost = (uint8 *)0; + ROMBaseHost = RAMBaseHost + RAMSize; + } + else +#endif + { + uint8 *ram_rom_area = (uint8 *)vm_acquire_mac(RAMSize + 0x100000); + if (ram_rom_area == VM_MAP_FAILED) { + ErrorAlert(STR_NO_MEM_ERR); + QuitEmulator(); + } + RAMBaseHost = ram_rom_area; + ROMBaseHost = RAMBaseHost + RAMSize; + } - // Create area for Mac ROM - ROMBaseHost = new uint8[0x100000]; +#if USE_SCRATCHMEM_SUBTERFUGE + // Allocate scratch memory + ScratchMem = (uint8 *)vm_acquire_mac(SCRATCH_MEM_SIZE); + if (ScratchMem == VM_MAP_FAILED) { + ErrorAlert(STR_NO_MEM_ERR); + QuitEmulator(); + } + ScratchMem += SCRATCH_MEM_SIZE/2; // ScratchMem points to middle of block +#endif +#if DIRECT_ADDRESSING + // RAMBaseMac shall always be zero + MEMBaseDiff = (uintptr)RAMBaseHost; + RAMBaseMac = 0; + ROMBaseMac = Host2MacAddr(ROMBaseHost); +#endif +#if REAL_ADDRESSING + RAMBaseMac = Host2MacAddr(RAMBaseHost); + ROMBaseMac = Host2MacAddr(ROMBaseHost); +#endif + D(bug("Mac RAM starts at %p (%08x)\n", RAMBaseHost, RAMBaseMac)); + D(bug("Mac ROM starts at %p (%08x)\n", ROMBaseHost, ROMBaseMac)); + // Get rom file path from preferences const char *rom_path = PrefsFindString("rom"); // Load Mac ROM int rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY); if (rom_fd < 0) { - ErrorAlert(GetString(STR_NO_ROM_FILE_ERR)); + ErrorAlert(STR_NO_ROM_FILE_ERR); QuitEmulator(); } printf(GetString(STR_READING_ROM_FILE)); ROMSize = lseek(rom_fd, 0, SEEK_END); if (ROMSize != 64*1024 && ROMSize != 128*1024 && ROMSize != 256*1024 && ROMSize != 512*1024 && ROMSize != 1024*1024) { - ErrorAlert(GetString(STR_ROM_SIZE_ERR)); + ErrorAlert(STR_ROM_SIZE_ERR); close(rom_fd); QuitEmulator(); } lseek(rom_fd, 0, SEEK_SET); if (read(rom_fd, ROMBaseHost, ROMSize) != (ssize_t)ROMSize) { - ErrorAlert(GetString(STR_ROM_FILE_READ_ERR)); + ErrorAlert(STR_ROM_FILE_READ_ERR); close(rom_fd); QuitEmulator(); } - // Check ROM version - if (!CheckROM()) { - ErrorAlert(GetString(STR_UNSUPPORTED_ROM_TYPE_ERR)); - QuitEmulator(); - } +#if !EMULATED_68K + // Get CPU model + int mib[2] = {CTL_HW, HW_MODEL}; + char *model; + size_t model_len; + sysctl(mib, 2, NULL, &model_len, NULL, 0); + model = (char *)malloc(model_len); + sysctl(mib, 2, model, &model_len, NULL, 0); + D(bug("Model: %s\n", model)); - // Set CPU and FPU type (UAE emulation) - switch (ROMVersion) { - case ROM_VERSION_64K: - case ROM_VERSION_PLUS: - case ROM_VERSION_CLASSIC: - CPUType = 0; - FPUType = 0; - TwentyFourBitAddressing = true; - break; - case ROM_VERSION_II: - CPUType = 2; - FPUType = PrefsFindBool("fpu") ? 1 : 0; - TwentyFourBitAddressing = true; - break; - case ROM_VERSION_32: - CPUType = 3; - FPUType = PrefsFindBool("fpu") ? 1 : 0; - TwentyFourBitAddressing = false; - break; - } + // Set CPU and FPU type CPUIs68060 = false; + if (strstr(model, "020")) + CPUType = 2; + else if (strstr(model, "030")) + CPUType = 3; + else if (strstr(model, "040")) + CPUType = 4; + else if (strstr(model, "060")) { + CPUType = 4; + CPUIs68060 = true; + } else { + printf("WARNING: Cannot detect CPU type, assuming 68020\n"); + CPUType = 2; + } + FPUType = 1; // NetBSD has an FPU emulation, so the FPU ought to be available at all times + TwentyFourBitAddressing = false; +#endif - // Load XPRAM - XPRAMInit(); - - // Set boot volume - int16 i16 = PrefsFindInt16("bootdrive"); - XPRAM[0x78] = i16 >> 8; - XPRAM[0x79] = i16 & 0xff; - i16 = PrefsFindInt16("bootdriver"); - XPRAM[0x7a] = i16 >> 8; - XPRAM[0x7b] = i16 & 0xff; - - // Start XPRAM watchdog thread - xpram_thread_active = (pthread_create(&xpram_thread, NULL, xpram_func, NULL) == 0); - - // Init drivers - SonyInit(); - DiskInit(); - CDROMInit(); - SCSIInit(); - - // Init serial ports - SerialInit(); - - // Init network - EtherInit(); - - // Init Time Manager - TimerInit(); + // Initialize everything + if (!InitAll()) + QuitEmulator(); + D(bug("Initialization complete\n")); - // Init clipboard - ClipInit(); +#if !EMULATED_68K + // (Virtual) supervisor mode, disable interrupts + EmulatedSR = 0x2700; + +#ifdef HAVE_PTHREADS + // Get handle of main thread + emul_thread = pthread_self(); +#endif - // Init audio - AudioInit(); + // Create and install stack for signal handlers + sig_stack = malloc(SIG_STACK_SIZE); + D(bug("Signal stack at %p\n", sig_stack)); + if (sig_stack == NULL) { + ErrorAlert(STR_NOT_ENOUGH_MEMORY_ERR); + QuitEmulator(); + } + stack_t new_stack; + new_stack.ss_sp = sig_stack; + new_stack.ss_flags = 0; + new_stack.ss_size = SIG_STACK_SIZE; + if (sigaltstack(&new_stack, NULL) < 0) { + sprintf(str, GetString(STR_SIGALTSTACK_ERR), strerror(errno)); + ErrorAlert(str); + QuitEmulator(); + } - // Init video - if (!VideoInit(ROMVersion == ROM_VERSION_64K || ROMVersion == ROM_VERSION_PLUS || ROMVersion == ROM_VERSION_CLASSIC)) + // Install SIGILL handler for emulating privileged instructions and + // executing A-Trap and EMUL_OP opcodes + sigemptyset(&sigill_sa.sa_mask); // Block virtual 68k interrupts during SIGILL handling + sigaddset(&sigill_sa.sa_mask, SIG_IRQ); + sigaddset(&sigill_sa.sa_mask, SIGALRM); + sigill_sa.sa_handler = (void (*)(int))sigill_handler; + sigill_sa.sa_flags = SA_ONSTACK; + if (sigaction(SIGILL, &sigill_sa, NULL) < 0) { + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGILL", strerror(errno)); + ErrorAlert(str); QuitEmulator(); + } - // Init 680x0 emulation (this also activates the memory system which is needed for PatchROM()) - if (!Init680x0()) + // Install virtual 68k interrupt signal handler + sigemptyset(&sigirq_sa.sa_mask); + sigaddset(&sigirq_sa.sa_mask, SIGALRM); + sigirq_sa.sa_handler = (void (*)(int))sigirq_handler; + sigirq_sa.sa_flags = SA_ONSTACK | SA_RESTART; + if (sigaction(SIG_IRQ, &sigirq_sa, NULL) < 0) { + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIG_IRQ", strerror(errno)); + ErrorAlert(str); QuitEmulator(); + } +#endif - // Install ROM patches - if (!PatchROM()) { - ErrorAlert(GetString(STR_UNSUPPORTED_ROM_TYPE_ERR)); +#ifdef ENABLE_MON + // Setup SIGINT handler to enter mon + sigemptyset(&sigint_sa.sa_mask); + sigint_sa.sa_handler = (void (*)(int))sigint_handler; + sigint_sa.sa_flags = 0; + sigaction(SIGINT, &sigint_sa, NULL); +#endif + +#ifndef USE_CPU_EMUL_SERVICES +#if defined(HAVE_PTHREADS) + + // POSIX threads available, start 60Hz thread + Set_pthread_attr(&tick_thread_attr, 0); + tick_thread_active = (pthread_create(&tick_thread, &tick_thread_attr, tick_func, NULL) == 0); + if (!tick_thread_active) { + sprintf(str, GetString(STR_TICK_THREAD_ERR), strerror(errno)); + ErrorAlert(str); QuitEmulator(); } + D(bug("60Hz thread started\n")); -#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) - // Start 60Hz timer +#elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) + + // POSIX.4 timers and real-time signals available, start 60Hz timer sigemptyset(&timer_sa.sa_mask); + timer_sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))one_tick; timer_sa.sa_flags = SA_SIGINFO | SA_RESTART; - timer_sa.sa_sigaction = one_tick; if (sigaction(SIG_TIMER, &timer_sa, NULL) < 0) { - printf("FATAL: cannot set up timer signal handler\n"); + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIG_TIMER", strerror(errno)); + ErrorAlert(str); QuitEmulator(); } struct sigevent timer_event; timer_event.sigev_notify = SIGEV_SIGNAL; timer_event.sigev_signo = SIG_TIMER; if (timer_create(CLOCK_REALTIME, &timer_event, &timer) < 0) { - printf("FATAL: cannot create timer\n"); + sprintf(str, GetString(STR_TIMER_CREATE_ERR), strerror(errno)); + ErrorAlert(str); QuitEmulator(); } struct itimerspec req; @@ -305,32 +773,44 @@ int main(int argc, char **argv) req.it_value.tv_nsec = 16625000; req.it_interval.tv_sec = 0; req.it_interval.tv_nsec = 16625000; - if (timer_settime(timer, TIMER_RELTIME, &req, NULL) < 0) { - printf("FATAL: cannot start timer\n"); + if (timer_settime(timer, 0, &req, NULL) < 0) { + sprintf(str, GetString(STR_TIMER_SETTIME_ERR), strerror(errno)); + ErrorAlert(str); QuitEmulator(); } + D(bug("60Hz timer started\n")); #else - // Start 60Hz thread - pthread_attr_init(&tick_thread_attr); -#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) - if (geteuid() == 0) { - pthread_attr_setinheritsched(&tick_thread_attr, PTHREAD_EXPLICIT_SCHED); - pthread_attr_setschedpolicy(&tick_thread_attr, SCHED_FIFO); - struct sched_param fifo_param; - fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2; - pthread_attr_setschedparam(&tick_thread_attr, &fifo_param); - } + // Start 60Hz timer + sigemptyset(&timer_sa.sa_mask); // Block virtual 68k interrupts during SIGARLM handling +#if !EMULATED_68K + sigaddset(&timer_sa.sa_mask, SIG_IRQ); #endif - tick_thread_active = (pthread_create(&tick_thread, &tick_thread_attr, tick_func, NULL) == 0); - if (!tick_thread_active) { - printf("FATAL: cannot create tick thread\n"); + timer_sa.sa_handler = one_tick; + timer_sa.sa_flags = SA_ONSTACK | SA_RESTART; + if (sigaction(SIGALRM, &timer_sa, NULL) < 0) { + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGALRM", strerror(errno)); + ErrorAlert(str); QuitEmulator(); } + struct itimerval req; + req.it_interval.tv_sec = req.it_value.tv_sec = 0; + req.it_interval.tv_usec = req.it_value.tv_usec = 16625; + setitimer(ITIMER_REAL, &req, NULL); + +#endif +#endif + +#ifdef USE_PTHREADS_SERVICES + // Start XPRAM watchdog thread + memcpy(last_xpram, XPRAM, XPRAM_SIZE); + xpram_thread_active = (pthread_create(&xpram_thread, NULL, xpram_func, NULL) == 0); + D(bug("XPRAM thread started\n")); #endif // Start 68k and jump to ROM boot routine + D(bug("Starting emulation...\n")); Start680x0(); QuitEmulator(); @@ -344,13 +824,20 @@ int main(int argc, char **argv) void QuitEmulator(void) { + D(bug("QuitEmulator\n")); + +#if EMULATED_68K // Exit 680x0 emulation Exit680x0(); +#endif -#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) - // Stop 60Hz timer - timer_delete(timer); -#else +#if defined(USE_CPU_EMUL_SERVICES) + // Show statistics + uint64 emulated_ticks_end = GetTicks_usec(); + D(bug("%ld ticks in %ld usec = %f ticks/sec [%ld tick checks]\n", + (long)emulated_ticks_count, (long)(emulated_ticks_end - emulated_ticks_start), + emulated_ticks_count * 1000000.0 / (emulated_ticks_end - emulated_ticks_start), (long)n_check_ticks)); +#elif defined(USE_PTHREADS_SERVICES) // Stop 60Hz thread if (tick_thread_active) { tick_thread_cancel = true; @@ -359,8 +846,17 @@ void QuitEmulator(void) #endif pthread_join(tick_thread, NULL); } +#elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) + // Stop 60Hz timer + timer_delete(timer); +#else + struct itimerval req; + req.it_interval.tv_sec = req.it_value.tv_sec = 0; + req.it_interval.tv_usec = req.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &req, NULL); #endif +#ifdef USE_PTHREADS_SERVICES // Stop XPRAM watchdog thread if (xpram_thread_active) { xpram_thread_cancel = true; @@ -369,39 +865,34 @@ void QuitEmulator(void) #endif pthread_join(xpram_thread, NULL); } +#endif - // Save XPRAM - XPRAMExit(); - - // Exit video - VideoExit(); - - // Exit audio - AudioExit(); - - // Exit clipboard - ClipExit(); - - // Exit Time Manager - TimerExit(); - - // Exit serial ports - SerialExit(); - - // Exit network - EtherExit(); + // Deinitialize everything + ExitAll(); - // Exit drivers - SCSIExit(); - CDROMExit(); - DiskExit(); - SonyExit(); + // Free ROM/RAM areas + if (RAMBaseHost != VM_MAP_FAILED) { + vm_release(RAMBaseHost, RAMSize + 0x100000); + RAMBaseHost = NULL; + ROMBaseHost = NULL; + } - // Delete ROM area - delete[] ROMBaseHost; +#if USE_SCRATCHMEM_SUBTERFUGE + // Delete scratch memory area + if (ScratchMem != (uint8 *)VM_MAP_FAILED) { + vm_release((void *)(ScratchMem - SCRATCH_MEM_SIZE/2), SCRATCH_MEM_SIZE); + ScratchMem = NULL; + } +#endif - // Delete RAM area - delete[] RAMBaseHost; +#if REAL_ADDRESSING + // Delete Low Memory area + if (lm_area_mapped) + vm_release(0, 0x2000); +#endif + + // Exit VM wrappers + vm_exit(); // Exit system routines SysExit(); @@ -410,8 +901,10 @@ void QuitEmulator(void) PrefsExit(); // Close X11 server connection +#ifndef USE_SDL_VIDEO if (x_display) XCloseDisplay(x_display); +#endif exit(0); } @@ -422,10 +915,147 @@ void QuitEmulator(void) * or a dynamically recompiling emulator) */ -#if EMULATED_68K void FlushCodeCache(void *start, uint32 size) { +#if USE_JIT + if (UseJIT) + flush_icache_range((uintptr)start, size); +#endif +#if !EMULATED_68K && defined(__NetBSD__) + m68k_sync_icache(start, size); +#endif +} + + +/* + * SIGINT handler, enters mon + */ + +#ifdef ENABLE_MON +static void sigint_handler(...) +{ +#if EMULATED_68K + uaecptr nextpc; + extern void m68k_dumpstate(uaecptr *nextpc); + m68k_dumpstate(&nextpc); +#endif + VideoQuitFullScreen(); + char *arg[4] = {"mon", "-m", "-r", NULL}; + mon(3, arg); + QuitEmulator(); +} +#endif + + +#ifdef HAVE_PTHREADS +/* + * Pthread configuration + */ + +void Set_pthread_attr(pthread_attr_t *attr, int priority) +{ + pthread_attr_init(attr); +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) + // Some of these only work for superuser + if (geteuid() == 0) { + pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(attr, SCHED_FIFO); + struct sched_param fifo_param; + fifo_param.sched_priority = ((sched_get_priority_min(SCHED_FIFO) + + sched_get_priority_max(SCHED_FIFO)) / 2 + + priority); + pthread_attr_setschedparam(attr, &fifo_param); + } + if (pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) != 0) { +#ifdef PTHREAD_SCOPE_BOUND_NP + // If system scope is not available (eg. we're not running + // with CAP_SCHED_MGT capability on an SGI box), try bound + // scope. It exposes pthread scheduling to the kernel, + // without setting realtime priority. + pthread_attr_setscope(attr, PTHREAD_SCOPE_BOUND_NP); +#endif + } +#endif +} +#endif // HAVE_PTHREADS + + +/* + * Mutexes + */ + +#ifdef HAVE_PTHREADS + +struct B2_mutex { + B2_mutex() { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + // Initialize the mutex for priority inheritance -- + // required for accurate timing. +#if defined(HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL) && !defined(__CYGWIN__) + pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); +#endif +#if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL) + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); +#endif +#ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); +#endif + pthread_mutex_init(&m, &attr); + pthread_mutexattr_destroy(&attr); + } + ~B2_mutex() { + pthread_mutex_trylock(&m); // Make sure it's locked before + pthread_mutex_unlock(&m); // unlocking it. + pthread_mutex_destroy(&m); + } + pthread_mutex_t m; +}; + +B2_mutex *B2_create_mutex(void) +{ + return new B2_mutex; +} + +void B2_lock_mutex(B2_mutex *mutex) +{ + pthread_mutex_lock(&mutex->m); +} + +void B2_unlock_mutex(B2_mutex *mutex) +{ + pthread_mutex_unlock(&mutex->m); +} + +void B2_delete_mutex(B2_mutex *mutex) +{ + delete mutex; +} + +#else + +struct B2_mutex { + int dummy; +}; + +B2_mutex *B2_create_mutex(void) +{ + return new B2_mutex; +} + +void B2_lock_mutex(B2_mutex *mutex) +{ +} + +void B2_unlock_mutex(B2_mutex *mutex) +{ +} + +void B2_delete_mutex(B2_mutex *mutex) +{ + delete mutex; } + #endif @@ -435,35 +1065,103 @@ void FlushCodeCache(void *start, uint32 uint32 InterruptFlags = 0; +#if EMULATED_68K void SetInterruptFlag(uint32 flag) { - pthread_mutex_lock(&intflag_lock); + LOCK_INTFLAGS; InterruptFlags |= flag; - pthread_mutex_unlock(&intflag_lock); + UNLOCK_INTFLAGS; } void ClearInterruptFlag(uint32 flag) { - pthread_mutex_lock(&intflag_lock); + LOCK_INTFLAGS; InterruptFlags &= ~flag; - pthread_mutex_unlock(&intflag_lock); + UNLOCK_INTFLAGS; +} +#endif + +#if !EMULATED_68K +void TriggerInterrupt(void) +{ +#if defined(HAVE_PTHREADS) + pthread_kill(emul_thread, SIG_IRQ); +#else + raise(SIG_IRQ); +#endif +} + +void TriggerNMI(void) +{ + // not yet supported +} +#endif + + +/* + * XPRAM watchdog thread (saves XPRAM every minute) + */ + +static void xpram_watchdog(void) +{ + if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) { + memcpy(last_xpram, XPRAM, XPRAM_SIZE); + SaveXPRAM(); + } +} + +#ifdef USE_PTHREADS_SERVICES +static void *xpram_func(void *arg) +{ + while (!xpram_thread_cancel) { + for (int i=0; i<60 && !xpram_thread_cancel; i++) + Delay_usec(999999); // Only wait 1 second so we quit promptly when xpram_thread_cancel becomes true + xpram_watchdog(); + } + return NULL; } +#endif /* * 60Hz thread (really 60.15Hz) */ +static void one_second(void) +{ + // Pseudo Mac 1Hz interrupt, update local time + WriteMacInt32(0x20c, TimerDateTime()); + + SetInterruptFlag(INTFLAG_1HZ); + TriggerInterrupt(); + +#ifndef USE_PTHREADS_SERVICES + static int second_counter = 0; + if (++second_counter > 60) { + second_counter = 0; + xpram_watchdog(); + } +#endif +} + static void one_tick(...) { static int tick_counter = 0; - - // Pseudo Mac 1Hz interrupt, update local time if (++tick_counter > 60) { tick_counter = 0; - WriteMacInt32(0x20c, TimerDateTime()); + one_second(); } +#ifndef USE_PTHREADS_SERVICES + // Threads not used to trigger interrupts, perform video refresh from here + VideoRefresh(); +#endif + +#ifndef HAVE_PTHREADS + // No threads available, perform networking from here + SetInterruptFlag(INTFLAG_ETHER); +#endif + // Trigger 60Hz interrupt if (ROMVersion != ROM_VERSION_CLASSIC || HasMacStarted()) { SetInterruptFlag(INTFLAG_60HZ); @@ -471,57 +1169,339 @@ static void one_tick(...) } } +#ifdef USE_PTHREADS_SERVICES static void *tick_func(void *arg) { + uint64 start = GetTicks_usec(); + int64 ticks = 0; + uint64 next = GetTicks_usec(); while (!tick_thread_cancel) { - - // Wait -#ifdef HAVE_NANOSLEEP - struct timespec req = {0, 16625000}; - nanosleep(&req, NULL); -#else - usleep(16625); -#endif - - // Action one_tick(); + next += 16625; + int64 delay = next - GetTicks_usec(); + if (delay > 0) + Delay_usec(delay); + else if (delay < -16625) + next = GetTicks_usec(); + ticks++; } + uint64 end = GetTicks_usec(); + D(bug("%lld ticks in %lld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start))); return NULL; } +#endif +#if !EMULATED_68K /* - * XPRAM watchdog thread (saves XPRAM every minute) + * Virtual 68k interrupt handler */ -void *xpram_func(void *arg) +static void sigirq_handler(int sig, int code, struct sigcontext *scp) { - uint8 last_xpram[256]; - memcpy(last_xpram, XPRAM, 256); + // Interrupts disabled? Then do nothing + if (EmulatedSR & 0x0700) + return; - while (!xpram_thread_cancel) { - for (int i=0; i<60 && !xpram_thread_cancel; i++) { -#ifdef HAVE_NANOSLEEP - struct timespec req = {1, 0}; - nanosleep(&req, NULL); -#else - usleep(1000000); -#endif + struct sigstate *state = (struct sigstate *)scp->sc_ap; + M68kRegisters *regs = (M68kRegisters *)&state->ss_frame; + + // Set up interrupt frame on stack + uint32 a7 = regs->a[7]; + a7 -= 2; + WriteMacInt16(a7, 0x64); + a7 -= 4; + WriteMacInt32(a7, scp->sc_pc); + a7 -= 2; + WriteMacInt16(a7, scp->sc_ps | EmulatedSR); + scp->sc_sp = regs->a[7] = a7; + + // Set interrupt level + EmulatedSR |= 0x2100; + + // Jump to MacOS interrupt handler on return + scp->sc_pc = ReadMacInt32(0x64); +} + + +/* + * SIGILL handler, for emulation of privileged instructions and executing + * A-Trap and EMUL_OP opcodes + */ + +static void sigill_handler(int sig, int code, struct sigcontext *scp) +{ + struct sigstate *state = (struct sigstate *)scp->sc_ap; + uint16 *pc = (uint16 *)scp->sc_pc; + uint16 opcode = *pc; + M68kRegisters *regs = (M68kRegisters *)&state->ss_frame; + +#define INC_PC(n) scp->sc_pc += (n) + +#define GET_SR (scp->sc_ps | EmulatedSR) + +#define STORE_SR(v) \ + scp->sc_ps = (v) & 0xff; \ + EmulatedSR = (v) & 0xe700; \ + if (((v) & 0x0700) == 0 && InterruptFlags) \ + TriggerInterrupt(); + +//printf("opcode %04x at %p, sr %04x, emul_sr %04x\n", opcode, pc, scp->sc_ps, EmulatedSR); + + if ((opcode & 0xf000) == 0xa000) { + + // A-Line instruction, set up A-Line trap frame on stack + uint32 a7 = regs->a[7]; + a7 -= 2; + WriteMacInt16(a7, 0x28); + a7 -= 4; + WriteMacInt32(a7, (uint32)pc); + a7 -= 2; + WriteMacInt16(a7, GET_SR); + scp->sc_sp = regs->a[7] = a7; + + // Jump to MacOS A-Line handler on return + scp->sc_pc = ReadMacInt32(0x28); + + } else if ((opcode & 0xff00) == 0x7100) { + + // Extended opcode, push registers on user stack + uint32 a7 = regs->a[7]; + a7 -= 4; + WriteMacInt32(a7, (uint32)pc); + a7 -= 2; + WriteMacInt16(a7, scp->sc_ps); + for (int i=7; i>=0; i--) { + a7 -= 4; + WriteMacInt32(a7, regs->a[i]); + } + for (int i=7; i>=0; i--) { + a7 -= 4; + WriteMacInt32(a7, regs->d[i]); + } + scp->sc_sp = regs->a[7] = a7; + + // Jump to EmulOp trampoline code on return + scp->sc_pc = (uint32)EmulOpTrampoline; + + } else switch (opcode) { // Emulate privileged instructions + + case 0x40e7: // move sr,-(sp) + regs->a[7] -= 2; + WriteMacInt16(regs->a[7], GET_SR); + scp->sc_sp = regs->a[7]; + INC_PC(2); + break; + + case 0x46df: { // move (sp)+,sr + uint16 sr = ReadMacInt16(regs->a[7]); + STORE_SR(sr); + regs->a[7] += 2; + scp->sc_sp = regs->a[7]; + INC_PC(2); + break; + } + + case 0x007c: { // ori #xxxx,sr + uint16 sr = GET_SR | pc[1]; + scp->sc_ps = sr & 0xff; // oring bits into the sr can't enable interrupts, so we don't need to call STORE_SR + EmulatedSR = sr & 0xe700; + INC_PC(4); + break; + } + + case 0x027c: { // andi #xxxx,sr + uint16 sr = GET_SR & pc[1]; + STORE_SR(sr); + INC_PC(4); + break; + } + + case 0x46fc: // move #xxxx,sr + STORE_SR(pc[1]); + INC_PC(4); + break; + + case 0x46ef: { // move (xxxx,sp),sr + uint16 sr = ReadMacInt16(regs->a[7] + (int32)(int16)pc[1]); + STORE_SR(sr); + INC_PC(4); + break; + } + + case 0x46d8: // move (a0)+,sr + case 0x46d9: { // move (a1)+,sr + uint16 sr = ReadMacInt16(regs->a[opcode & 7]); + STORE_SR(sr); + regs->a[opcode & 7] += 2; + INC_PC(2); + break; + } + + case 0x40f8: // move sr,xxxx.w + WriteMacInt16(pc[1], GET_SR); + INC_PC(4); + break; + + case 0x40d0: // move sr,(a0) + case 0x40d1: // move sr,(a1) + case 0x40d2: // move sr,(a2) + case 0x40d3: // move sr,(a3) + case 0x40d4: // move sr,(a4) + case 0x40d5: // move sr,(a5) + case 0x40d6: // move sr,(a6) + case 0x40d7: // move sr,(sp) + WriteMacInt16(regs->a[opcode & 7], GET_SR); + INC_PC(2); + break; + + case 0x40c0: // move sr,d0 + case 0x40c1: // move sr,d1 + case 0x40c2: // move sr,d2 + case 0x40c3: // move sr,d3 + case 0x40c4: // move sr,d4 + case 0x40c5: // move sr,d5 + case 0x40c6: // move sr,d6 + case 0x40c7: // move sr,d7 + regs->d[opcode & 7] = GET_SR; + INC_PC(2); + break; + + case 0x46c0: // move d0,sr + case 0x46c1: // move d1,sr + case 0x46c2: // move d2,sr + case 0x46c3: // move d3,sr + case 0x46c4: // move d4,sr + case 0x46c5: // move d5,sr + case 0x46c6: // move d6,sr + case 0x46c7: { // move d7,sr + uint16 sr = regs->d[opcode & 7]; + STORE_SR(sr); + INC_PC(2); + break; } - if (memcmp(last_xpram, XPRAM, 256)) { - memcpy(last_xpram, XPRAM, 256); - SaveXPRAM(); + + case 0xf327: // fsave -(sp) + regs->a[7] -= 4; + WriteMacInt32(regs->a[7], 0x41000000); // Idle frame + scp->sc_sp = regs->a[7]; + INC_PC(2); + break; + + case 0xf35f: // frestore (sp)+ + regs->a[7] += 4; + scp->sc_sp = regs->a[7]; + INC_PC(2); + break; + + case 0x4e73: { // rte + uint32 a7 = regs->a[7]; + uint16 sr = ReadMacInt16(a7); + a7 += 2; + scp->sc_ps = sr & 0xff; + EmulatedSR = sr & 0xe700; + scp->sc_pc = ReadMacInt32(a7); + a7 += 4; + uint16 format = ReadMacInt16(a7) >> 12; + a7 += 2; + static const int frame_adj[16] = { + 0, 0, 4, 4, 8, 0, 0, 52, 50, 12, 24, 84, 16, 0, 0, 0 + }; + scp->sc_sp = regs->a[7] = a7 + frame_adj[format]; + break; } + + case 0x4e7a: // movec cr,x + switch (pc[1]) { + case 0x0002: // movec cacr,d0 + regs->d[0] = 0x3111; + break; + case 0x1002: // movec cacr,d1 + regs->d[1] = 0x3111; + break; + case 0x0003: // movec tc,d0 + case 0x0004: // movec itt0,d0 + case 0x0005: // movec itt1,d0 + case 0x0006: // movec dtt0,d0 + case 0x0007: // movec dtt1,d0 + case 0x0806: // movec urp,d0 + case 0x0807: // movec srp,d0 + regs->d[0] = 0; + break; + case 0x1000: // movec sfc,d1 + case 0x1001: // movec dfc,d1 + case 0x1003: // movec tc,d1 + case 0x1801: // movec vbr,d1 + regs->d[1] = 0; + break; + case 0x8801: // movec vbr,a0 + regs->a[0] = 0; + break; + case 0x9801: // movec vbr,a1 + regs->a[1] = 0; + break; + default: + goto ill; + } + INC_PC(4); + break; + + case 0x4e7b: // movec x,cr + switch (pc[1]) { + case 0x1000: // movec d1,sfc + case 0x1001: // movec d1,dfc + case 0x0801: // movec d0,vbr + case 0x1801: // movec d1,vbr + break; + case 0x0002: // movec d0,cacr + case 0x1002: // movec d1,cacr + FlushCodeCache(NULL, 0); + break; + default: + goto ill; + } + INC_PC(4); + break; + + case 0xf478: // cpusha dc + case 0xf4f8: // cpusha dc/ic + FlushCodeCache(NULL, 0); + INC_PC(2); + break; + + default: +ill: printf("SIGILL num %d, code %d\n", sig, code); + printf(" context %p:\n", scp); + printf(" onstack %08x\n", scp->sc_onstack); + printf(" sp %08x\n", scp->sc_sp); + printf(" fp %08x\n", scp->sc_fp); + printf(" pc %08x\n", scp->sc_pc); + printf(" opcode %04x\n", opcode); + printf(" sr %08x\n", scp->sc_ps); + printf(" state %p:\n", state); + printf(" flags %d\n", state->ss_flags); + for (int i=0; i<8; i++) + printf(" d%d %08x\n", i, state->ss_frame.f_regs[i]); + for (int i=0; i<8; i++) + printf(" a%d %08x\n", i, state->ss_frame.f_regs[i+8]); + + VideoQuitFullScreen(); +#ifdef ENABLE_MON + char *arg[4] = {"mon", "-m", "-r", NULL}; + mon(3, arg); +#endif + QuitEmulator(); + break; } - return NULL; } +#endif /* * Display alert */ -#if ENABLE_GTK +#ifdef ENABLE_GTK static void dl_destroyed(void) { gtk_main_quit(); @@ -566,7 +1546,7 @@ void display_alert(int title_id, int pre void ErrorAlert(const char *text) { -#if ENABLE_GTK +#if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO) if (PrefsFindBool("nogui") || x_display == NULL) { printf(GetString(STR_SHELL_ERROR_PREFIX), text); return; @@ -585,7 +1565,7 @@ void ErrorAlert(const char *text) void WarningAlert(const char *text) { -#if ENABLE_GTK +#if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO) if (PrefsFindBool("nogui") || x_display == NULL) { printf(GetString(STR_SHELL_WARNING_PREFIX), text); return;