/* fuse.c: The Free Unix Spectrum Emulator Copyright (c) 1999-2017 Philip Kendall and others 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Author contact information: E-mail: philip-fuse@shadowmagic.org.uk */ #include #include #include #include #include #include #include #include #ifdef WIN32 #include #endif /* #ifdef WIN32 */ #include /* We need to include SDL.h on Mac O X and Windows to do some magic bootstrapping by redefining main. As we now allow SDL joystick code to be used in the GTK+ and Xlib UIs we need to also do the magic when that code is in use, feel free to look away for the next line */ #if defined UI_SDL || (defined USE_JOYSTICK && !defined HAVE_JSW_H && (defined UI_X || defined UI_GTK) ) #include /* Needed on MacOS X and Windows */ #endif /* #if defined UI_SDL || (defined USE_JOYSTICK && !defined HAVE_JSW_H && (defined UI_X || defined UI_GTK) ) */ #ifdef GEKKO #include #endif /* #ifdef GEKKO */ #ifdef HAVE_LIB_XML2 #include #endif #include "debugger/debugger.h" #include "display.h" #include "event.h" #include "fuse.h" #include "infrastructure/startup_manager.h" #include "keyboard.h" #include "machine.h" #include "machines/machines_periph.h" #include "memory_pages.h" #include "module.h" #include "movie.h" #include "mempool.h" #include "peripherals/ay.h" #include "peripherals/dck.h" #include "peripherals/disk/beta.h" #include "peripherals/disk/didaktik.h" #include "peripherals/disk/fdd.h" #include "peripherals/fuller.h" #include "peripherals/ide/divide.h" #include "peripherals/ide/divmmc.h" #include "peripherals/ide/simpleide.h" #include "peripherals/ide/zxatasp.h" #include "peripherals/ide/zxcf.h" #include "peripherals/ide/zxmmc.h" #include "peripherals/joystick.h" #include "peripherals/if1.h" #include "peripherals/if2.h" #include "peripherals/kempmouse.h" #include "peripherals/melodik.h" #include "peripherals/multiface.h" #include "peripherals/printer.h" #include "peripherals/scld.h" #include "peripherals/speccyboot.h" #include "peripherals/spectranet.h" #include "peripherals/ula.h" #include "peripherals/usource.h" #include "phantom_typist.h" #include "pokefinder/pokemem.h" #include "profile.h" #include "psg.h" #include "rzx.h" #include "settings.h" #include "slt.h" #include "snapshot.h" #include "sound.h" #include "spectrum.h" #include "tape.h" #include "timer/timer.h" #include "ui/scaler/scaler.h" #include "ui/ui.h" #include "ui/uimedia.h" #include "unittests/unittests.h" #include "utils.h" #include "z80/z80.h" /* What name were we called under? */ const char *fuse_progname; /* A flag to say when we want to exit the emulator */ int fuse_exiting; /* Is Spectrum emulation currently paused, and if so, how many times? */ int fuse_emulation_paused; /* The creator information we'll store in file formats that support this */ libspectrum_creator *fuse_creator; /* The earliest version of libspectrum we need */ static const char * const LIBSPECTRUM_MIN_VERSION = "0.5.0"; /* The various types of file we may want to run on startup */ typedef struct start_files_t { const char *disk_plus3; const char *disk_opus; const char *disk_plusd; const char *disk_beta; const char *disk_didaktik80; const char *disk_disciple; const char *dock; const char *if2; const char *playback; const char *recording; const char *snapshot; const char *tape; const char *simpleide_master, *simpleide_slave; const char *zxatasp_master, *zxatasp_slave; const char *zxcf; const char *divide_master, *divide_slave; const char *divmmc; const char *zxmmc; const char *mdr[8]; } start_files_t; /* Context for the display startup routine */ static display_startup_context display_context; static int fuse_init(int argc, char **argv); static void creator_register_startup( void ); static void fuse_show_copyright(void); static void fuse_show_version( void ); static void fuse_show_help( void ); static int setup_start_files( start_files_t *start_files ); static int parse_nonoption_args( int argc, char **argv, int first_arg, start_files_t *start_files ); static int do_start_files( start_files_t *start_files ); static int fuse_end(void); #ifdef UI_WIN32 int fuse_main(int argc, char **argv) #else int main(int argc, char **argv) #endif { int r; #ifdef WIN32 SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX ); #endif #ifdef GEKKO fatInitDefault(); #endif /* #ifdef GEKKO */ if(fuse_init(argc,argv)) { fprintf(stderr,"%s: error initialising -- giving up!\n", fuse_progname); return 1; } if( settings_current.show_help || settings_current.show_version ) return 0; if( settings_current.unittests ) { r = unittests_run(); } else { while( !fuse_exiting ) { z80_do_opcodes(); event_do_events(); } r = debugger_get_exit_code(); } fuse_end(); return r; } static int fuse_libspectrum_init( void *context ) { if( libspectrum_check_version( LIBSPECTRUM_MIN_VERSION ) ) { if( libspectrum_init() ) return 1; } else { ui_error( UI_ERROR_ERROR, "libspectrum version %s found, but %s required", libspectrum_version(), LIBSPECTRUM_MIN_VERSION ); return 1; } return 0; } static void libspectrum_register_startup( void ) { startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_DISPLAY }; startup_manager_register( STARTUP_MANAGER_MODULE_LIBSPECTRUM, dependencies, ARRAY_SIZE( dependencies ), fuse_libspectrum_init, NULL, NULL ); } static int libxml2_init( void *context ) { #ifdef HAVE_LIB_XML2 LIBXML_TEST_VERSION #endif return 0; } static void libxml2_register_startup( void ) { startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_SETUID }; startup_manager_register( STARTUP_MANAGER_MODULE_LIBXML2, dependencies, ARRAY_SIZE( dependencies ), libxml2_init, NULL, NULL ); } static int setuid_init( void *context ) { #ifdef HAVE_GETEUID int error; /* Drop root privs if we have them */ if( !geteuid() ) { error = setuid( getuid() ); if( error ) { ui_error( UI_ERROR_ERROR, "Could not drop root privileges" ); return 1; } } #endif /* #ifdef HAVE_GETEUID */ return 0; } static void setuid_register_startup() { startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_DISPLAY, STARTUP_MANAGER_MODULE_LIBSPECTRUM, }; startup_manager_register( STARTUP_MANAGER_MODULE_SETUID, dependencies, ARRAY_SIZE( dependencies ), setuid_init, NULL, NULL ); } static int run_startup_manager( int *argc, char ***argv ) { startup_manager_init(); display_context.argc = argc; display_context.argv = argv; /* Get every module to register its init function */ ay_register_startup(); beta_register_startup(); creator_register_startup(); covox_register_startup(); debugger_register_startup(); didaktik80_register_startup(); disciple_register_startup(); display_register_startup( &display_context ); divide_register_startup(); divmmc_register_startup(); event_register_startup(); fdd_register_startup(); fuller_register_startup(); if1_register_startup(); if2_register_startup(); joystick_register_startup(); kempmouse_register_startup(); keyboard_register_startup(); libspectrum_register_startup(); libxml2_register_startup(); machine_register_startup(); machines_periph_register_startup(); melodik_register_startup(); memory_register_startup(); mempool_register_startup(); multiface_register_startup(); opus_register_startup(); phantom_typist_register_startup(); plusd_register_startup(); printer_register_startup(); profile_register_startup(); psg_register_startup(); rzx_register_startup(); scld_register_startup(); settings_register_startup(); setuid_register_startup(); simpleide_register_startup(); slt_register_startup(); sound_register_startup(); speccyboot_register_startup(); specdrum_register_startup(); spectranet_register_startup(); spectrum_register_startup(); tape_register_startup(); timer_register_startup(); ula_register_startup(); usource_register_startup(); z80_register_startup(); zxatasp_register_startup(); zxcf_register_startup(); zxmmc_register_startup(); return startup_manager_run(); } static int fuse_init(int argc, char **argv) { int error, first_arg; char *start_scaler; start_files_t start_files; /* Seed the bad but widely-available random number generator with the current time */ srand( (unsigned)time( NULL ) ); /* Some platforms (e.g. Wii) do not have argc/argv */ if(argc > 0) fuse_progname = argv[0]; else fuse_progname = "fuse"; libspectrum_error_function = ui_libspectrum_error; #ifdef GEKKO /* On the Wii, init the display first so we have a way of outputting messages */ if( display_init(&argc,&argv) ) return 1; #endif if( settings_init( &first_arg, argc, argv ) ) return 1; if( settings_current.show_version ) { fuse_show_version(); return 0; } else if( settings_current.show_help ) { fuse_show_help(); return 0; } start_scaler = utils_safe_strdup( settings_current.start_scaler_mode ); fuse_show_copyright(); if( run_startup_manager( &argc, &argv ) ) return 1; error = machine_select_id( settings_current.start_machine ); if( error ) return error; error = scaler_select_id( start_scaler ); libspectrum_free( start_scaler ); if( error ) return error; if( setup_start_files( &start_files ) ) return 1; if( parse_nonoption_args( argc, argv, first_arg, &start_files ) ) return 1; if( do_start_files( &start_files ) ) return 1; /* Must do this after all subsytems are initialised */ debugger_command_evaluate( settings_current.debugger_command ); if( ui_mouse_present ) ui_mouse_grabbed = ui_mouse_grab( 1 ); fuse_emulation_paused = 0; movie_init(); return 0; } static int creator_init( void *context ) { size_t i; unsigned int version[4] = { 0, 0, 0, 0 }; char *custom, osname[ 256 ]; static const size_t CUSTOM_SIZE = 256; libspectrum_error error; int sys_error; const char *gcrypt_version; sscanf( VERSION, "%u.%u.%u.%u", &version[0], &version[1], &version[2], &version[3] ); for( i=0; i<4; i++ ) if( version[i] > 0xff ) version[i] = 0xff; sys_error = compat_osname( osname, sizeof( osname ) ); if( sys_error ) return 1; fuse_creator = libspectrum_creator_alloc(); error = libspectrum_creator_set_program( fuse_creator, "Fuse" ); if( error ) { libspectrum_creator_free( fuse_creator ); return error; } error = libspectrum_creator_set_major( fuse_creator, version[0] * 0x100 + version[1] ); if( error ) { libspectrum_creator_free( fuse_creator ); return error; } error = libspectrum_creator_set_minor( fuse_creator, version[2] * 0x100 + version[3] ); if( error ) { libspectrum_creator_free( fuse_creator ); return error; } custom = libspectrum_new( char, CUSTOM_SIZE ); gcrypt_version = libspectrum_gcrypt_version(); if( !gcrypt_version ) gcrypt_version = "not available"; snprintf( custom, CUSTOM_SIZE, "gcrypt: %s\nlibspectrum: %s\nuname: %s", gcrypt_version, libspectrum_version(), osname ); error = libspectrum_creator_set_custom( fuse_creator, (libspectrum_byte*)custom, strlen( custom ) ); if( error ) { libspectrum_free( custom ); libspectrum_creator_free( fuse_creator ); return error; } return 0; } static void creator_end( void ) { libspectrum_creator_free( fuse_creator ); } static void creator_register_startup( void ) { startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_SETUID }; startup_manager_register( STARTUP_MANAGER_MODULE_CREATOR, dependencies, ARRAY_SIZE( dependencies ), creator_init, NULL, creator_end ); } static void fuse_show_copyright(void) { printf( "\n" ); fuse_show_version(); printf( FUSE_COPYRIGHT "; see the file\n" "'AUTHORS' for more details.\n" "\n" "For help, please mail or use\n" "the forums at .\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n\n"); } static void fuse_show_version( void ) { printf( "The Free Unix Spectrum Emulator (Fuse) version " VERSION ".\n" ); } static void fuse_show_help( void ) { printf( "\n" ); fuse_show_version(); printf( "\nAvailable command-line options:\n\n" "Boolean options (use `--no-