--- BasiliskII/src/MacOSX/main_macosx.mm 2003/03/26 00:26:38 1.7 +++ BasiliskII/src/MacOSX/main_macosx.mm 2008/02/04 01:00:53 1.20 @@ -1,11 +1,11 @@ /* - * $Id: main_macosx.mm,v 1.7 2003/03/26 00:26:38 nigel Exp $ + * $Id: main_macosx.mm,v 1.20 2008/02/04 01:00:53 nigel Exp $ * * main_macosx.mm - Startup code for MacOS X * Based (in a small way) on the default main.m, and on Basilisk's main_unix.cpp * - * Basilisk II (C) 1997-2002 Christian Bauer + * Basilisk II (C) 1997-2008 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 @@ -21,7 +21,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define PTHREADS + +#import +#undef check + +#define PTHREADS // Why is this here? #include "sysdeps.h" #ifdef HAVE_PTHREADS @@ -32,20 +36,27 @@ # include #endif +#include +using std::string; + #include "cpu_emulation.h" #include "macos_util_macosx.h" #include "main.h" #include "prefs.h" #include "prefs_editor.h" #include "rom_patches.h" +#include "sigsegv.h" #include "sys.h" -#include "timer.h" #include "user_strings.h" #include "version.h" #include "video.h" #include "vm_alloc.h" #include "xpram.h" +#if USE_JIT +extern void flush_icache_range(uint8 *start, uint32 size); // from compemu_support.cpp +#endif + #ifdef ENABLE_MON # include "mon.h" #endif @@ -54,17 +65,17 @@ #include "debug.h" -#import - #include "main_macosx.h" // To bridge between main() and misc. classes // Constants const char ROM_FILE_NAME[] = "ROM"; -const int SIG_STACK_SIZE = SIGSTKSZ; // Size of signal stack const int SCRATCH_MEM_SIZE = 0x10000; // Size of scratch memory area +static char *bundle = NULL; // If in an OS X application bundle, its path + + // CPU and FPU type, addressing mode int CPUType; bool CPUIs68060; @@ -91,11 +102,6 @@ static pthread_mutex_t intflag_lock = PT uint8 *ScratchMem = NULL; // Scratch memory for Mac ROM writes #endif -#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) -#define SIG_TIMER SIGRTMIN -static timer_t timer; // 60Hz timer -#endif - #ifdef ENABLE_MON static struct sigaction sigint_sa; // sigaction for SIGINT handler static void sigint_handler(...); @@ -106,6 +112,87 @@ static bool lm_area_mapped = false; // F #endif +/* + * Helpers to map memory that can be accessed from the Mac side + */ + +// NOTE: VM_MAP_32BIT is only used when compiling a 64-bit JIT on specific platforms +void *vm_acquire_mac(size_t size) +{ + return vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_32BIT); +} + +static int vm_acquire_mac_fixed(void *addr, size_t size) +{ + return vm_acquire_fixed(addr, size, VM_MAP_DEFAULT | VM_MAP_32BIT); +} + + +/* + * SIGSEGV handler + */ + +static sigsegv_return_t sigsegv_handler(sigsegv_info_t *sip) +{ + const uintptr fault_address = (uintptr)sigsegv_get_fault_address(sip); +#if ENABLE_VOSF + // Handle screen fault + extern bool Screen_fault_handler(sigsegv_info_t *sip); + if (Screen_fault_handler(sip)) + 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_info_t *sip) +{ + const sigsegv_address_t fault_address = sigsegv_get_fault_address(sip); + const sigsegv_address_t fault_instruction = sigsegv_get_fault_instruction_address(sip); + fprintf(stderr, "Caught SIGSEGV at address %p", fault_address); + if (fault_instruction != SIGSEGV_INVALID_ADDRESS) + fprintf(stderr, " [IP=%p]", fault_instruction); + fprintf(stderr, "\n"); + uaecptr nextpc; + extern void m68k_dumpstate(uaecptr *nextpc); + m68k_dumpstate(&nextpc); +#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(); +} + + +/* + * Screen fault handler + */ + +bool Screen_fault_handler(sigsegv_info_t *sip) +{ + return true; +} + /* * Main program @@ -116,8 +203,10 @@ static void usage(const char *prg_name) printf("Usage: %s [OPTION...]\n", prg_name); printf("\nUnix options:\n"); printf(" --help\n display this usage message\n"); + printf(" --config FILE\n read/write configuration from/to FILE\n"); printf(" --break ADDRESS\n set ROM breakpoint\n"); printf(" --rominfo\n dump ROM information\n"); + LoadPrefs(); // read the prefs file so PrefsPrintUsage() will print the correct default values PrefsPrintUsage(); exit(0); } @@ -134,9 +223,6 @@ int main(int argc, char **argv) printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR); printf(" %s\n", GetString(STR_ABOUT_TEXT2)); - // Read preferences - PrefsInit(argc, argv); - // Parse command line arguments for (int i=1; i 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 @@ -196,23 +318,25 @@ bool InitEmulator (void) #if REAL_ADDRESSING // Flag: RAM and ROM are contigously allocated from address 0 bool memory_mapped_from_zero = false; - - // Under Solaris/SPARC and NetBSD/m68k, Basilisk II is known to crash - // when trying to map a too big chunk of memory starting at address 0 -#if defined(OS_solaris) || defined(OS_netbsd) - const bool can_map_all_memory = false; -#else + + // 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_fixed(0, RAMSize + 0x100000) == 0)) { + 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_fixed(0, 0x2000) == 0) { + else if (vm_acquire_mac_fixed(0, 0x2000) == 0) { D(bug("Could allocate the Low Memory globals\n")); lm_area_mapped = true; } @@ -223,9 +347,10 @@ bool InitEmulator (void) ErrorAlert(str); QuitEmulator(); } +#endif #else *str = 0; // Eliminate unused variable warning -#endif +#endif /* REAL_ADDRESSING */ // Create areas for Mac RAM and ROM #if REAL_ADDRESSING @@ -236,17 +361,18 @@ bool InitEmulator (void) else #endif { - RAMBaseHost = (uint8 *)vm_acquire(RAMSize); - ROMBaseHost = (uint8 *)vm_acquire(0x100000); - if (RAMBaseHost == VM_MAP_FAILED || ROMBaseHost == VM_MAP_FAILED) { + 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; } #if USE_SCRATCHMEM_SUBTERFUGE // Allocate scratch memory - ScratchMem = (uint8 *)vm_acquire(SCRATCH_MEM_SIZE); + ScratchMem = (uint8 *)vm_acquire_mac(SCRATCH_MEM_SIZE); if (ScratchMem == VM_MAP_FAILED) { ErrorAlert(STR_NO_MEM_ERR); QuitEmulator(); @@ -261,8 +387,8 @@ bool InitEmulator (void) ROMBaseMac = Host2MacAddr(ROMBaseHost); #endif #if REAL_ADDRESSING - RAMBaseMac = (uint32)RAMBaseHost; - ROMBaseMac = (uint32)ROMBaseHost; + 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)); @@ -270,6 +396,9 @@ bool InitEmulator (void) // Get rom file path from preferences const char *rom_path = PrefsFindString("rom"); if ( ! rom_path ) + if ( bundle ) + WarningAlert("No rom pathname set. Trying BasiliskII.app/ROM"); + else WarningAlert("No rom pathname set. Trying ./ROM"); // Load Mac ROM @@ -330,11 +459,8 @@ void QuitEmuNoExit() // Free ROM/RAM areas if (RAMBaseHost != VM_MAP_FAILED) { - vm_release(RAMBaseHost, RAMSize); + vm_release(RAMBaseHost, RAMSize + 0x100000); RAMBaseHost = NULL; - } - if (ROMBaseHost != VM_MAP_FAILED) { - vm_release(ROMBaseHost, 0x100000); ROMBaseHost = NULL; } @@ -364,9 +490,6 @@ void QuitEmuNoExit() void QuitEmulator(void) { - extern NSApplication *NSApp; - - QuitEmuNoExit(); // Stop run loop? @@ -383,6 +506,10 @@ void QuitEmulator(void) void FlushCodeCache(void *start, uint32 size) { +#if USE_JIT + if (UseJIT) + flush_icache_range((uint8 *)start, size); +#endif } @@ -404,6 +531,39 @@ static void sigint_handler(...) #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 */ @@ -411,8 +571,28 @@ static void sigint_handler(...) #ifdef HAVE_PTHREADS struct B2_mutex { - B2_mutex() { pthread_mutex_init(&m, NULL); } - ~B2_mutex() { pthread_mutex_unlock(&m); pthread_mutex_destroy(&m); } + B2_mutex() { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + // Initialize the mutex for priority inheritance -- + // required for accurate timing. +#ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL + 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; }; @@ -495,9 +675,10 @@ void ErrorAlert(const char *text) NSString *error = [NSString stringWithCString: text]; NSString *button = [NSString stringWithCString: GetString(STR_QUIT_BUTTON) ]; -// If we have a full screen mode, quit it here? - NSLog(error); + if ( PrefsFindBool("nogui") ) + return; + VideoQuitFullScreen(); NSRunCriticalAlertPanel(title, error, button, nil, nil); } @@ -514,6 +695,9 @@ void WarningAlert(const char *text) NSString *button = [NSString stringWithCString: GetString(STR_OK_BUTTON) ]; NSLog(warning); + if ( PrefsFindBool("nogui") ) + return; + VideoQuitFullScreen(); NSRunAlertPanel(title, warning, button, nil, nil); } @@ -530,6 +714,5 @@ bool ChoiceAlert(const char *text, const NSString *yes = [NSString stringWithCString: pos]; NSString *no = [NSString stringWithCString: neg]; - NSLog(warning); return NSRunInformationalAlertPanel(title, warning, yes, no, nil); }