--- BasiliskII/src/Unix/main_unix.cpp 2000/11/30 16:20:52 1.27 +++ 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-2000 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 @@ -24,7 +24,14 @@ #include #include #include -#include + +#ifdef USE_SDL +# include +#endif + +#ifndef USE_SDL_VIDEO +# include +#endif #ifdef HAVE_PTHREADS # include @@ -50,6 +57,10 @@ struct sigstate { #ifdef ENABLE_GTK # include +# include +# ifdef HAVE_GNOMEUI +# include +# endif #endif #ifdef ENABLE_XF86_DGA @@ -57,6 +68,9 @@ struct sigstate { # include #endif +#include +using std::string; + #include "cpu_emulation.h" #include "sys.h" #include "rom_patches.h" @@ -70,6 +84,12 @@ struct sigstate { #include "user_strings.h" #include "version.h" #include "main.h" +#include "vm_alloc.h" +#include "sigsegv.h" + +#if USE_JIT +extern void flush_icache_range(uint32 start, uint32 size); // from compemu_support.cpp +#endif #ifdef ENABLE_MON # include "mon.h" @@ -81,7 +101,9 @@ struct sigstate { // 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 @@ -101,17 +123,24 @@ int CPUType; bool CPUIs68060; int FPUType; bool TwentyFourBitAddressing; +bool ThirtyThreeBitAddressing = false; // Global variables -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 int zero_fd = -1; // FD of /dev/zero -static uint8 last_xpram[256]; // Buffer for monitoring XPRAM changes +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 @@ -122,9 +151,15 @@ static volatile bool tick_thread_cancel static pthread_t tick_thread; // 60Hz thread static pthread_attr_t tick_thread_attr; // 60Hz thread attributes -#if EMULATED_68K static pthread_mutex_t intflag_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect InterruptFlags -#endif +#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 @@ -139,12 +174,14 @@ uint16 EmulatedSR; // Emulated bits 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 timer_t timer; // 60Hz timer #endif +#endif // !HAVE_PTHREADS #ifdef ENABLE_MON static struct sigaction sigint_sa; // sigaction for SIGINT handler @@ -153,11 +190,6 @@ static void sigint_handler(...); #if REAL_ADDRESSING static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped -static bool memory_mapped_from_zero = false; // Flag: Could allocate RAM area from 0 -#endif - -#if REAL_ADDRESSING || DIRECT_ADDRESSING -static uint32 mapped_ram_rom_size; // Total size of mmap()ed RAM/ROM area #endif @@ -191,9 +223,172 @@ 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]; @@ -208,16 +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 - const uint32 page_size = getpagesize(); - const uint32 page_mask = page_size - 1; - const uint32 aligned_ram_size = (RAMSize + page_mask) & ~page_mask; - mapped_ram_rom_size = aligned_ram_size + 0x100000; + 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 - // Try to allocate the complete address space from zero - // gb-- the Solaris manpage about mmap(2) states that using MAP_FIXED - // implies undefined behaviour for further use of sbrk(), malloc(), etc. - // cebix-- on NetBSD/m68k, this causes a segfault -#if defined(OS_solaris) || defined(OS_netbsd) - // Anyway, it doesn't work... - if (0) { + // 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 - if (mmap((caddr_t)0x0000, mapped_ram_rom_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, zero_fd, 0) != MAP_FAILED) { + 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; } - // Create Low Memory area (0x0000..0x2000) - else if (mmap((char *)0x0000, 0x2000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, zero_fd, 0) != MAP_FAILED) { + +#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 error + + // Exit on failure else { sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno)); ErrorAlert(str); QuitEmulator(); } #endif - -#if USE_SCRATCHMEM_SUBTERFUGE - // Allocate scratch memory - ScratchMem = (uint8 *)malloc(SCRATCH_MEM_SIZE); - if (ScratchMem == NULL) { - ErrorAlert(GetString(STR_NO_MEM_ERR)); - QuitEmulator(); - } - ScratchMem += SCRATCH_MEM_SIZE/2; // ScratchMem points to middle of block -#endif +#endif /* REAL_ADDRESSING */ // Create areas for Mac RAM and ROM -#if REAL_ADDRESSING || DIRECT_ADDRESSING - // gb-- Overkill, needs to be cleaned up. Probably explode it for either - // real or direct addressing mode. #if REAL_ADDRESSING if (memory_mapped_from_zero) { RAMBaseHost = (uint8 *)0; - ROMBaseHost = RAMBaseHost + aligned_ram_size; + ROMBaseHost = RAMBaseHost + RAMSize; } else #endif { - RAMBaseHost = (uint8 *)mmap(0, mapped_ram_rom_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0); - if (RAMBaseHost == (uint8 *)MAP_FAILED) { - ErrorAlert(GetString(STR_NO_MEM_ERR)); + uint8 *ram_rom_area = (uint8 *)vm_acquire_mac(RAMSize + 0x100000); + if (ram_rom_area == VM_MAP_FAILED) { + ErrorAlert(STR_NO_MEM_ERR); QuitEmulator(); } - ROMBaseHost = RAMBaseHost + aligned_ram_size; + RAMBaseHost = ram_rom_area; + ROMBaseHost = RAMBaseHost + RAMSize; } -#else - RAMBaseHost = (uint8 *)malloc(RAMSize); - ROMBaseHost = (uint8 *)malloc(0x100000); - if (RAMBaseHost == NULL || ROMBaseHost == NULL) { - ErrorAlert(GetString(STR_NO_MEM_ERR)); + +#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 - // Initialize MEMBaseDiff now so that Host2MacAddr in the Video module - // will return correct results + // RAMBaseMac shall always be zero + MEMBaseDiff = (uintptr)RAMBaseHost; RAMBaseMac = 0; - ROMBaseMac = RAMBaseMac + aligned_ram_size; - InitMEMBaseDiff(RAMBaseHost, RAMBaseMac); + ROMBaseMac = Host2MacAddr(ROMBaseHost); #endif -#if REAL_ADDRESSING // && !EMULATED_68K - RAMBaseMac = (uint32)RAMBaseHost; - ROMBaseMac = (uint32)ROMBaseHost; +#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)); @@ -356,19 +626,19 @@ int main(int argc, char **argv) // 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(); } @@ -407,20 +677,20 @@ int main(int argc, char **argv) QuitEmulator(); D(bug("Initialization complete\n")); +#if !EMULATED_68K + // (Virtual) supervisor mode, disable interrupts + EmulatedSR = 0x2700; + #ifdef HAVE_PTHREADS // Get handle of main thread emul_thread = pthread_self(); #endif -#if !EMULATED_68K - // (Virtual) supervisor mode, disable interrupts - EmulatedSR = 0x2700; - // 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(GetString(STR_NOT_ENOUGH_MEMORY_ERR)); + ErrorAlert(STR_NOT_ENOUGH_MEMORY_ERR); QuitEmulator(); } stack_t new_stack; @@ -466,7 +736,20 @@ int main(int argc, char **argv) sigaction(SIGINT, &sigint_sa, NULL); #endif -#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) +#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")); + +#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); @@ -497,32 +780,13 @@ int main(int argc, char **argv) } D(bug("60Hz timer started\n")); -#elif defined(HAVE_PTHREADS) - - // POSIX threads available, 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); - } -#endif - 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")); - #else // 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 timer_sa.sa_handler = one_tick; timer_sa.sa_flags = SA_ONSTACK | SA_RESTART; if (sigaction(SIGALRM, &timer_sa, NULL) < 0) { @@ -536,10 +800,11 @@ int main(int argc, char **argv) setitimer(ITIMER_REAL, &req, NULL); #endif +#endif -#ifdef HAVE_PTHREADS +#ifdef USE_PTHREADS_SERVICES // Start XPRAM watchdog thread - memcpy(last_xpram, XPRAM, 256); + 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 @@ -566,10 +831,13 @@ void QuitEmulator(void) Exit680x0(); #endif -#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) - // Stop 60Hz timer - timer_delete(timer); -#elif defined(HAVE_PTHREADS) +#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; @@ -578,6 +846,9 @@ 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; @@ -585,7 +856,7 @@ void QuitEmulator(void) setitimer(ITIMER_REAL, &req, NULL); #endif -#ifdef HAVE_PTHREADS +#ifdef USE_PTHREADS_SERVICES // Stop XPRAM watchdog thread if (xpram_thread_active) { xpram_thread_cancel = true; @@ -600,31 +871,16 @@ void QuitEmulator(void) ExitAll(); // Free ROM/RAM areas -#if REAL_ADDRESSING - if (memory_mapped_from_zero) - munmap((caddr_t)0x0000, mapped_ram_rom_size); - else -#endif -#if REAL_ADDRESSING || DIRECT_ADDRESSING - if (RAMBaseHost != (uint8 *)MAP_FAILED) { - munmap((caddr_t)RAMBaseHost, mapped_ram_rom_size); + if (RAMBaseHost != VM_MAP_FAILED) { + vm_release(RAMBaseHost, RAMSize + 0x100000); RAMBaseHost = NULL; - } -#else - if (ROMBaseHost) { - free(ROMBaseHost); ROMBaseHost = NULL; } - if (RAMBaseHost) { - free(RAMBaseHost); - RAMBaseHost = NULL; - } -#endif #if USE_SCRATCHMEM_SUBTERFUGE // Delete scratch memory area - if (ScratchMem) { - free((void *)(ScratchMem - SCRATCH_MEM_SIZE/2)); + if (ScratchMem != (uint8 *)VM_MAP_FAILED) { + vm_release((void *)(ScratchMem - SCRATCH_MEM_SIZE/2), SCRATCH_MEM_SIZE); ScratchMem = NULL; } #endif @@ -632,12 +888,11 @@ void QuitEmulator(void) #if REAL_ADDRESSING // Delete Low Memory area if (lm_area_mapped) - munmap((char *)0x0000, 0x2000); + vm_release(0, 0x2000); #endif - - // Close /dev/zero - if (zero_fd > 0) - close(zero_fd); + + // Exit VM wrappers + vm_exit(); // Exit system routines SysExit(); @@ -646,8 +901,10 @@ void QuitEmulator(void) PrefsExit(); // Close X11 server connection +#ifndef USE_SDL_VIDEO if (x_display) XCloseDisplay(x_display); +#endif exit(0); } @@ -660,6 +917,10 @@ void QuitEmulator(void) 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 @@ -677,12 +938,124 @@ static void sigint_handler(...) uaecptr nextpc; extern void m68k_dumpstate(uaecptr *nextpc); m68k_dumpstate(&nextpc); -#else +#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 @@ -695,24 +1068,16 @@ uint32 InterruptFlags = 0; #if EMULATED_68K void SetInterruptFlag(uint32 flag) { -#ifdef HAVE_PTHREADS - pthread_mutex_lock(&intflag_lock); + LOCK_INTFLAGS; InterruptFlags |= flag; - pthread_mutex_unlock(&intflag_lock); -#else - InterruptFlags |= flag; // Pray that this is an atomic operation... -#endif + UNLOCK_INTFLAGS; } void ClearInterruptFlag(uint32 flag) { -#ifdef HAVE_PTHREADS - pthread_mutex_lock(&intflag_lock); - InterruptFlags &= ~flag; - pthread_mutex_unlock(&intflag_lock); -#else + LOCK_INTFLAGS; InterruptFlags &= ~flag; -#endif + UNLOCK_INTFLAGS; } #endif @@ -739,18 +1104,18 @@ void TriggerNMI(void) static void xpram_watchdog(void) { - if (memcmp(last_xpram, XPRAM, 256)) { - memcpy(last_xpram, XPRAM, 256); + if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) { + memcpy(last_xpram, XPRAM, XPRAM_SIZE); SaveXPRAM(); } } -#ifdef HAVE_PTHREADS +#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); + Delay_usec(999999); // Only wait 1 second so we quit promptly when xpram_thread_cancel becomes true xpram_watchdog(); } return NULL; @@ -770,7 +1135,7 @@ static void one_second(void) SetInterruptFlag(INTFLAG_1HZ); TriggerInterrupt(); -#ifndef HAVE_PTHREADS +#ifndef USE_PTHREADS_SERVICES static int second_counter = 0; if (++second_counter > 60) { second_counter = 0; @@ -787,11 +1152,16 @@ static void one_tick(...) one_second(); } -#ifndef HAVE_PTHREADS - // No threads available, perform video refresh from here +#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); @@ -799,9 +1169,11 @@ static void one_tick(...) } } -#ifdef HAVE_PTHREADS +#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) { one_tick(); @@ -811,92 +1183,15 @@ static void *tick_func(void *arg) 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 -/* - * Get current value of microsecond timer - */ - -uint64 GetTicks_usec(void) -{ -#ifdef HAVE_CLOCK_GETTIME - struct timespec t; - clock_gettime(CLOCK_REALTIME, &t); - return (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000; -#else - struct timeval t; - gettimeofday(&t, NULL); - return (uint64)t.tv_sec * 1000000 + t.tv_usec; -#endif -} - - -/* - * Delay by specified number of microseconds (<1 second) - * (adapted from SDL_Delay() source; this function is designed to provide - * the highest accuracy possible) - */ - -#if defined(linux) -// Linux select() changes its timeout parameter upon return to contain -// the remaining time. Most other unixen leave it unchanged or undefined. -#define SELECT_SETS_REMAINING -#elif defined(__FreeBSD__) || defined(__sun__) || defined(sgi) -#define USE_NANOSLEEP -#endif - -void Delay_usec(uint32 usec) -{ - int was_error; - -#ifdef USE_NANOSLEEP - struct timespec elapsed, tv; -#else - struct timeval tv; -#ifndef SELECT_SETS_REMAINING - uint64 then, now, elapsed; -#endif -#endif - - // Set the timeout interval - Linux only needs to do this once -#ifdef SELECT_SETS_REMAINING - tv.tv_sec = 0; - tv.tv_usec = usec; -#elif defined(USE_NANOSLEEP) - elapsed.tv_sec = 0; - elapsed.tv_nsec = usec * 1000; -#else - then = GetTicks_usec(); -#endif - - do { - errno = 0; -#ifdef USE_NANOSLEEP - tv.tv_sec = elapsed.tv_sec; - tv.tv_nsec = elapsed.tv_nsec; - was_error = nanosleep(&tv, &elapsed); -#else -#ifndef SELECT_SETS_REMAINING - // Calculate the time interval left (in case of interrupt) - now = GetTicks_usec(); - elapsed = now - then; - then = now; - if (elapsed >= usec) - break; - usec -= elapsed; - tv.tv_sec = 0; - tv.tv_usec = usec; -#endif - was_error = select(0, NULL, NULL, NULL, &tv); -#endif - } while (was_error && (errno == EINTR)); -} - - #if !EMULATED_68K /* * Virtual 68k interrupt handler @@ -1087,26 +1382,14 @@ static void sigill_handler(int sig, int } case 0xf327: // fsave -(sp) - if (CPUIs68060) { - regs->a[7] -= 4; - WriteMacInt32(regs->a[7], 0x60000000); // Idle frame - regs->a[7] -= 4; - WriteMacInt32(regs->a[7], 0); - regs->a[7] -= 4; - WriteMacInt32(regs->a[7], 0); - } else { - regs->a[7] -= 4; - WriteMacInt32(regs->a[7], 0x41000000); // Idle frame - } + 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)+ - if (CPUIs68060) - regs->a[7] += 12; - else - regs->a[7] += 4; + regs->a[7] += 4; scp->sc_sp = regs->a[7]; INC_PC(2); break; @@ -1202,6 +1485,7 @@ ill: printf("SIGILL num %d, code %d\n", 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); @@ -1262,7 +1546,7 @@ void display_alert(int title_id, int pre void ErrorAlert(const char *text) { -#ifdef ENABLE_GTK +#if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO) if (PrefsFindBool("nogui") || x_display == NULL) { printf(GetString(STR_SHELL_ERROR_PREFIX), text); return; @@ -1281,7 +1565,7 @@ void ErrorAlert(const char *text) void WarningAlert(const char *text) { -#ifdef ENABLE_GTK +#if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO) if (PrefsFindBool("nogui") || x_display == NULL) { printf(GetString(STR_SHELL_WARNING_PREFIX), text); return;