/* * atari.c - main high-level routines * * Copyright (c) 1995-1998 David Firth * Copyright (c) 1998-2014 Atari800 development team (see DOC/CREDITS) * * This file is part of the Atari800 emulator project which emulates * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. * * Atari800 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. * * Atari800 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 Atari800; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _POSIX_C_SOURCE 199309L /* for nanosleep */ #include "afile.h" #include "config.h" #include #include #include #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_WINDOWS_H #include #endif #ifdef __EMX__ #define INCL_DOS #include #endif #ifdef __BEOS__ #include #endif #ifdef HAVE_LIBZ #include #endif #ifdef R_SERIAL #include #endif #ifdef SDL #include #endif #include "akey.h" #include "antic.h" #include "artifact.h" #include "atari.h" #include "binload.h" #include "cartridge.h" #include "cassette.h" #include "cfg.h" #include "cpu.h" #include "devices.h" #include "emuos.h" #include "esc.h" #include "gtia.h" #include "input.h" #include "log.h" #include "memory.h" #include "monitor.h" #ifdef IDE # include "ide.h" #endif #include "pia.h" #include "platform.h" #include "pokey.h" #include "rtime.h" #include "pbi.h" #include "sio.h" #include "sysrom.h" #include "util.h" #if !defined(BASIC) && !defined(CURSES_BASIC) #include "colours.h" #include "screen.h" #endif #ifndef BASIC #include "statesav.h" #ifndef __PLUS #include "ui.h" #endif #endif /* BASIC */ #if defined(SOUND) && !defined(__PLUS) #include "pokeysnd.h" #include "sndsave.h" #include "sound.h" #endif #ifdef R_IO_DEVICE #include "rdevice.h" #endif #ifdef __PLUS #ifdef _WX_ #include "export.h" #else /* _WX_ */ #include "globals.h" #include "macros.h" #include "display_win.h" #include "misc_win.h" #include "registry.h" #include "timing.h" #include "FileService.h" #include "Helpers.h" #endif /* _WX_ */ #endif /* __PLUS */ #ifdef PBI_BB #include "pbi_bb.h" #endif #if defined(PBI_XLD) || defined (VOICEBOX) #include "votraxsnd.h" #endif #ifdef XEP80_EMULATION #include "xep80.h" #endif #ifdef AF80 #include "af80.h" #endif #ifdef BIT3 #include "bit3.h" #endif #ifdef NTSC_FILTER #include "filter_ntsc.h" #endif #ifdef VOICEBOX #include "voicebox.h" #endif #if SUPPORTS_CHANGE_VIDEOMODE #include "videomode.h" #endif #ifdef SDL #include "sdl/init.h" #endif #ifdef DIRECTX #include "win32\main.h" #endif int Atari800_machine_type = Atari800_MACHINE_XLXE; int Atari800_builtin_basic = TRUE; int Atari800_keyboard_leds = FALSE; int Atari800_f_keys = FALSE; int Atari800_jumper; int Atari800_builtin_game = FALSE; int Atari800_keyboard_detached = FALSE; int Atari800_tv_mode = Atari800_TV_PAL; int Atari800_disable_basic = TRUE; int Atari800_os_version = -1; int verbose = FALSE; int Atari800_display_screen = FALSE; int Atari800_nframes = 0; int Atari800_refresh_rate = 1; int Atari800_collisions_in_skipped_frames = FALSE; int Atari800_turbo = FALSE; int Atari800_auto_frameskip = FALSE; #ifdef BENCHMARK static double benchmark_start_time; #endif int emuos_mode = 1; /* 0 = never use EmuOS, 1 = use EmuOS if real OS not available, 2 = always use EmuOS */ #ifdef HAVE_SIGNAL volatile sig_atomic_t sigint_flag = FALSE; static RETSIGTYPE sigint_handler(int num) { sigint_flag = TRUE; /* Avoid restoring the original signal handler. */ signal(SIGINT, sigint_handler); return; } #endif void Atari800_SetMachineType(int type) { Atari800_machine_type = type; if (Atari800_machine_type != Atari800_MACHINE_XLXE) { Atari800_builtin_basic = FALSE; Atari800_keyboard_leds = FALSE; Atari800_f_keys = FALSE; Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; } } void Atari800_UpdateKeyboardDetached(void) { if (Atari800_machine_type == Atari800_MACHINE_XLXE) { GTIA_TRIG[2] = !Atari800_keyboard_detached; if (Atari800_keyboard_detached && (GTIA_GRACTL & 4)) GTIA_TRIG_latch[2] = 0; } } void Atari800_UpdateJumper(void) { if (Atari800_machine_type == Atari800_MACHINE_XLXE) POKEY_POT_input[4] = Atari800_jumper ? 0 : 228; } void Atari800_Warmstart(void) { if (Atari800_machine_type == Atari800_MACHINE_800) { /* A real Axlon homebanks on reset */ /* XXX: what does Mosaic do? */ if (MEMORY_axlon_num_banks > 0) MEMORY_PutByte(0xcfff, 0); /* RESET key in 400/800 does not reset chips, but only generates RNMI interrupt */ ANTIC_NMIST = 0x3f; CPU_NMI(); } else { PBI_Reset(); PIA_Reset(); ANTIC_Reset(); /* CPU_Reset() must be after PIA_Reset(), because Reset routine vector must be read from OS ROM */ CPU_Reset(); /* note: POKEY and GTIA have no Reset pin */ } #ifdef __PLUS HandleResetEvent(); #endif } void Atari800_Coldstart(void) { PBI_Reset(); PIA_Reset(); ANTIC_Reset(); /* CPU_Reset() must be after PIA_Reset(), because Reset routine vector must be read from OS ROM */ CPU_Reset(); /* note: POKEY and GTIA have no Reset pin */ #ifdef __PLUS HandleResetEvent(); #endif /* reset cartridge to power-up state */ CARTRIDGE_ColdStart(); /* set Atari OS Coldstart flag */ MEMORY_dPutByte(0x244, 1); /* handle Option key (disable BASIC in XL/XE) and Start key (boot from cassette) */ GTIA_consol_override = 2; #ifdef AF80 if (AF80_enabled) { AF80_Reset(); AF80_InsertRightCartridge(); } #endif #ifdef BIT3 if (BIT3_enabled) { BIT3_Reset(); } #endif } int Atari800_LoadImage(const char *filename, UBYTE *buffer, int nbytes) { FILE *f; int len; f = fopen(filename, "rb"); if (f == NULL) { Log_print("Error loading ROM image: %s", filename); return FALSE; } len = fread(buffer, 1, nbytes, f); fclose(f); if (len != nbytes) { Log_print("Error reading %s", filename); return FALSE; } return TRUE; } #define COPY_EMUOS(padding) do { \ memset(MEMORY_os, 0, padding); \ memcpy(MEMORY_os + (padding), emuos_h, 0x2000); \ } while (0) static int load_roms(void) { if (Atari800_machine_type != Atari800_MACHINE_5200 && emuos_mode == 2) { COPY_EMUOS(Atari800_machine_type == Atari800_MACHINE_800 ? 0x800 : 0x2000); Atari800_os_version = -1; } else { int basic_ver, xegame_ver; SYSROM_ChooseROMs(Atari800_machine_type, MEMORY_ram_size, Atari800_tv_mode, &Atari800_os_version, &basic_ver, &xegame_ver); if (Atari800_os_version == -1 || !Atari800_LoadImage(SYSROM_roms[Atari800_os_version].filename, MEMORY_os, SYSROM_roms[Atari800_os_version].size)) { /* Missing OS ROM. */ Atari800_os_version = -1; if (Atari800_machine_type != Atari800_MACHINE_5200 && emuos_mode == 1) COPY_EMUOS(Atari800_machine_type == Atari800_MACHINE_800 ? 0x800 : 0x2000); else /* No OS ROM loaded. */ return FALSE; } else if (Atari800_machine_type != Atari800_MACHINE_5200) { /* OS ROM found, try loading BASIC. */ MEMORY_have_basic = basic_ver != -1 && Atari800_LoadImage(SYSROM_roms[basic_ver].filename, MEMORY_basic, SYSROM_roms[basic_ver].size); if (!MEMORY_have_basic) /* Missing BASIC ROM. Don't fail when it happens. */ Atari800_builtin_basic = FALSE; if (Atari800_builtin_game) { /* Try loading built-in XEGS game. */ if (xegame_ver == -1 || !Atari800_LoadImage(SYSROM_roms[xegame_ver].filename, MEMORY_xegame, SYSROM_roms[xegame_ver].size)) /* Missing XEGS game ROM. */ Atari800_builtin_game = FALSE; } } } MEMORY_xe_bank = 0; return TRUE; } int Atari800_InitialiseMachine(void) { ESC_ClearAll(); if (!load_roms()) return FALSE; Atari800_UpdateKeyboardDetached(); Atari800_UpdateJumper(); MEMORY_InitialiseMachine(); Devices_UpdatePatches(); return TRUE; } /* Initialise any modules before loading the config file. */ static void PreInitialise(void) { #if !defined(BASIC) && !defined(CURSES_BASIC) Colours_PreInitialise(); #endif #ifdef NTSC_FILTER FILTER_NTSC_PreInitialise(); #endif } int Atari800_Initialise(int *argc, char *argv[]) { int i, j; const char *run_direct = NULL; #ifndef BASIC const char *state_file = NULL; #endif #ifdef __PLUS /* Atari800Win PLus doesn't use configuration files, it reads configuration from the Registry */ #ifndef _WX_ int bUpdateRegistry = (*argc > 1); #endif int bTapeFile = FALSE; int nCartType = CARTRIDGE_main.type; /* It is necessary because of the CARTRIDGE_ColdStart (there must not be the registry-read value available at startup) */ CARTRIDGE_main.type = CARTRIDGE_NONE; #ifndef _WX_ /* Print the time info in the "Log file" window */ Misc_PrintTime(); /* Force screen refreshing */ g_nTestVal = _GetRefreshRate() - 1; g_ulAtariState = ATARI_UNINITIALIZED; #endif /* _WX_ */ PreInitialise(); #else /* __PLUS */ const char *rtconfig_filename = NULL; int got_config; int help_only = FALSE; PreInitialise(); if (*argc > 1) { for (i = j = 1; i < *argc; i++) { if (strcmp(argv[i], "-config") == 0) { if (i + 1 < *argc) rtconfig_filename = argv[++i]; else { Log_print("Missing argument for '%s'", argv[i]); return FALSE; } } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "-version") == 0 || strcmp(argv[i], "--version") == 0) { printf("%s\n", Atari800_TITLE); return FALSE; } else if (strcmp(argv[i], "--usage") == 0 || strcmp(argv[i], "--help") == 0) { argv[j++] = "-help"; } else if (strcmp(argv[i], "-verbose") == 0) { verbose = TRUE; } else { argv[j++] = argv[i]; } } *argc = j; } //LIBRETRO HACK //#ifndef ANDROID #if !defined(ANDROID) || defined(__LIBRETRO__) got_config = CFG_LoadConfig(rtconfig_filename); #else got_config = TRUE; /* pretend we got a config file -- not needed in Android */ #endif /* try to find ROM images if the configuration file is not found or it does not specify some ROM paths (blank paths count as specified) */ //LIBRETRO HACK //#ifndef ANDROID #if !defined(ANDROID) || defined(__LIBRETRO__) #if defined(__LIBRETRO__) && defined(ANDROID) //FIXME USE RETROSYSDIR SYSROM_FindInDir("/mnt/sdcard/atari800", TRUE); #endif SYSROM_FindInDir(".", TRUE); /* current directory */ #if defined(unix) || defined(__unix__) || defined(__linux__) SYSROM_FindInDir("/usr/share/atari800", TRUE); #endif if (*argc > 0 && argv[0] != NULL) { char atari800_exe_dir[FILENAME_MAX]; char atari800_exe_rom_dir[FILENAME_MAX]; /* the directory of the Atari800 program */ Util_splitpath(argv[0], atari800_exe_dir, NULL); SYSROM_FindInDir(atari800_exe_dir, TRUE); /* "rom" and "ROM" subdirectories of this directory */ Util_catpath(atari800_exe_rom_dir, atari800_exe_dir, "rom"); SYSROM_FindInDir(atari800_exe_rom_dir, TRUE); /* skip "ROM" on systems that are known to be case-insensitive */ #if !defined(DJGPP) && !defined(HAVE_WINDOWS_H) Util_catpath(atari800_exe_rom_dir, atari800_exe_dir, "ROM"); SYSROM_FindInDir(atari800_exe_rom_dir, TRUE); #endif } #endif /* ANDROID */ /* finally if nothing is found, set some defaults to make the configuration file easier to edit */ SYSROM_SetDefaults(); /* if no configuration file read, try to save one with the defaults */ if (!got_config) CFG_WriteConfig(); #endif /* __PLUS */ for (i = j = 1; i < *argc; i++) { if (strcmp(argv[i], "-atari") == 0) { Atari800_machine_type = Atari800_MACHINE_800; MEMORY_ram_size = 48; Atari800_builtin_basic = FALSE; Atari800_keyboard_leds = FALSE; Atari800_f_keys = FALSE; Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; } else if (strcmp(argv[i], "-1200") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 64; Atari800_builtin_basic = FALSE; Atari800_keyboard_leds = TRUE; Atari800_f_keys = TRUE; Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; } else if (strcmp(argv[i], "-xl") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 64; Atari800_builtin_basic = TRUE; Atari800_keyboard_leds = FALSE; Atari800_f_keys = FALSE; Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; } else if (strcmp(argv[i], "-xe") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 128; Atari800_builtin_basic = TRUE; Atari800_keyboard_leds = FALSE; Atari800_f_keys = FALSE; Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; } else if (strcmp(argv[i], "-320xe") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = MEMORY_RAM_320_COMPY_SHOP; Atari800_builtin_basic = TRUE; Atari800_keyboard_leds = FALSE; Atari800_f_keys = FALSE; Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; } else if (strcmp(argv[i], "-rambo") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = MEMORY_RAM_320_RAMBO; Atari800_builtin_basic = TRUE; Atari800_keyboard_leds = FALSE; Atari800_f_keys = FALSE; Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; } else if (strcmp(argv[i], "-xegs") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 64; Atari800_builtin_basic = TRUE; Atari800_keyboard_leds = FALSE; Atari800_f_keys = FALSE; Atari800_jumper = FALSE; Atari800_builtin_game = TRUE; } else if (strcmp(argv[i], "-5200") == 0) { Atari800_machine_type = Atari800_MACHINE_5200; MEMORY_ram_size = 16; Atari800_builtin_basic = FALSE; Atari800_keyboard_leds = FALSE; Atari800_f_keys = FALSE; Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; } else if (strcmp(argv[i], "-nobasic") == 0) Atari800_disable_basic = TRUE; else if (strcmp(argv[i], "-basic") == 0) Atari800_disable_basic = FALSE; else if (strcmp(argv[i], "-nopatch") == 0) ESC_enable_sio_patch = FALSE; else if (strcmp(argv[i], "-nopatchall") == 0) ESC_enable_sio_patch = Devices_enable_h_patch = Devices_enable_p_patch = Devices_enable_r_patch = FALSE; else if (strcmp(argv[i], "-pal") == 0) Atari800_tv_mode = Atari800_TV_PAL; else if (strcmp(argv[i], "-ntsc") == 0) Atari800_tv_mode = Atari800_TV_NTSC; else if (strcmp(argv[i], "-emuos") == 0) emuos_mode = 2; else if (strcmp(argv[i], "-c") == 0) { if (Atari800_machine_type == Atari800_MACHINE_800) MEMORY_ram_size = 52; } else if (strcmp(argv[i], "-axlon0f") == 0) { MEMORY_axlon_0f_mirror = TRUE; } #ifdef STEREO_SOUND else if (strcmp(argv[i], "-stereo") == 0) { POKEYSND_stereo_enabled = TRUE; } else if (strcmp(argv[i], "-nostereo") == 0) { POKEYSND_stereo_enabled = FALSE; } #endif /* STEREO_SOUND */ else if (strcmp(argv[i], "-turbo") == 0) { Atari800_turbo = TRUE; } else { /* parameters that take additional argument follow here */ int i_a = (i + 1 < *argc); /* is argument available? */ int a_m = FALSE; /* error, argument missing! */ if (strcmp(argv[i], "-run") == 0) { if (i_a) run_direct = argv[++i]; else a_m = TRUE; } #ifdef R_IO_DEVICE else if (strcmp(argv[i], "-rdevice") == 0) { Devices_enable_r_patch = TRUE; #ifdef R_SERIAL if (i_a && i + 2 < *argc && *argv[i + 1] != '-') { /* optional serial device name */ struct stat statbuf; if (! stat(argv[i + 1], &statbuf)) { if (S_ISCHR(statbuf.st_mode)) { /* only accept devices as serial device */ Util_strlcpy(RDevice_serial_device, argv[++i], FILENAME_MAX); RDevice_serial_enabled = TRUE; } } } #endif /* R_SERIAL */ } #endif else if (strcmp(argv[i], "-mosaic") == 0) { if (i_a) { int total_ram = Util_sscandec(argv[++i]); MEMORY_mosaic_num_banks = (total_ram - 48)/4; if ((total_ram - 48) % 4 != 0 || MEMORY_mosaic_num_banks >= 0x40 || MEMORY_mosaic_num_banks < 0) { Log_print("Invalid Mosaic total RAM size"); return FALSE; } } else a_m = TRUE; } else if (strcmp(argv[i], "-axlon") == 0) { if (i_a) { int total_ram = Util_sscandec(argv[++i]); MEMORY_axlon_num_banks = ((total_ram) - 32) / 16; if ((total_ram - 32) % 16 != 0 || (MEMORY_axlon_num_banks != 0 && MEMORY_axlon_num_banks != 8 && MEMORY_axlon_num_banks != 16 && MEMORY_axlon_num_banks != 32 && MEMORY_axlon_num_banks != 64 && MEMORY_axlon_num_banks != 128 && MEMORY_axlon_num_banks != 256)) { Log_print("Invalid Axlon total RAM size"); return FALSE; } } else a_m = TRUE; } else if (strcmp(argv[i], "-mapram") == 0) MEMORY_enable_mapram = TRUE; else if (strcmp(argv[i], "-no-mapram") == 0) MEMORY_enable_mapram = FALSE; #ifndef BASIC /* The BASIC version does not support state files, because: 1. It has no ability to save state files, because of lack of UI. 2. It uses a simplified emulation, so the state files would be incompatible with other versions. 3. statesav is not compiled in to make the executable smaller. */ else if (strcmp(argv[i], "-state") == 0) { if (i_a) state_file = argv[++i]; else a_m = TRUE; } else if (strcmp(argv[i], "-refresh") == 0) { if (i_a) { Atari800_refresh_rate = Util_sscandec(argv[++i]); if (Atari800_refresh_rate < 1) { Log_print("Invalid refresh rate, using 1"); Atari800_refresh_rate = 1; } } else a_m = TRUE; } else if (strcmp(argv[i], "-autosave-config") == 0) CFG_save_on_exit = TRUE; else if (strcmp(argv[i], "-no-autosave-config") == 0) CFG_save_on_exit = FALSE; #endif /* BASIC */ else { /* all options known to main module tried but none matched */ if (strcmp(argv[i], "-help") == 0) { #ifndef __PLUS help_only = TRUE; Log_print("\t-config Specify alternate configuration file"); #endif Log_print("\t-autosave-config Automatically save configuration on emulator exit"); Log_print("\t-no-autosave-config"); Log_print("\t Disable automatic saving of configuration"); Log_print("\t-atari Emulate Atari 800"); Log_print("\t-1200 Emulate Atari 1200XL"); Log_print("\t-xl Emulate Atari 800XL"); Log_print("\t-xe Emulate Atari 130XE"); Log_print("\t-320xe Emulate Atari 320XE (Compy-Shop)"); Log_print("\t-rambo Emulate Atari 320XE (Rambo XL)"); Log_print("\t-xegs Emulate Atari XEGS"); Log_print("\t-5200 Emulate Atari 5200 Games System"); Log_print("\t-nobasic Turn off Atari BASIC ROM"); Log_print("\t-basic Turn on Atari BASIC ROM"); Log_print("\t-pal Enable PAL TV mode"); Log_print("\t-ntsc Enable NTSC TV mode"); Log_print("\t-run Run Atari program (COM, EXE, XEX, BAS, LST)"); #ifndef BASIC Log_print("\t-state Load saved-state file"); Log_print("\t-refresh Specify screen refresh rate"); #endif Log_print("\t-nopatch Don't patch SIO routine in OS"); Log_print("\t-nopatchall Don't patch OS at all, H: device won't work"); Log_print("\t-c Enable RAM between 0xc000 and 0xcfff in Atari 800"); Log_print("\t-axlon Use Atari 800 Axlon memory expansion: k total RAM"); Log_print("\t-axlon0f Use Axlon shadow at 0x0fc0-0x0fff"); Log_print("\t-mosaic Use 400/800 Mosaic memory expansion: k total RAM"); Log_print("\t-mapram Enable MapRAM for Atari XL/XE"); Log_print("\t-no-mapram Disable MapRAM"); #ifdef R_IO_DEVICE Log_print("\t-rdevice [] Enable R: emulation (using serial device )"); #endif Log_print("\t-turbo Run emulated Atari as fast as possible"); Log_print("\t-v Show version/release number"); } /* copy this option for platform/module specific evaluation */ argv[j++] = argv[i]; } /* this is the end of the additional argument check */ if (a_m) { printf("Missing argument for '%s'\n", argv[i]); return FALSE; } } } if (MEMORY_mosaic_num_banks > 0 && MEMORY_axlon_num_banks > 0) { Log_print("Axlon and Mosaic RAM can not both be enabled, because they are incompatible"); return FALSE; } *argc = j; /* Update machine feature variables. after reading from config file and/or command-line options. */ Atari800_SetMachineType(Atari800_machine_type); #ifdef SDL if (!help_only) { if (!SDL_INIT_Initialise()) return FALSE; } #endif /* SDL */ if (!SYSROM_Initialise(argc, argv) #if !defined(BASIC) && !defined(CURSES_BASIC) || !Colours_Initialise(argc, argv) || !ARTIFACT_Initialise(argc, argv) #endif || !Devices_Initialise(argc, argv) || !RTIME_Initialise(argc, argv) #ifdef IDE || !IDE_Initialise(argc, argv) #endif || !SIO_Initialise (argc, argv) || !CARTRIDGE_Initialise(argc, argv) || !CASSETTE_Initialise(argc, argv) || !PBI_Initialise(argc,argv) #ifdef VOICEBOX || !VOICEBOX_Initialise(argc, argv) #endif #ifndef BASIC || !INPUT_Initialise(argc, argv) #endif #ifdef XEP80_EMULATION || !XEP80_Initialise(argc, argv) #endif #ifdef AF80 || !AF80_Initialise(argc, argv) #endif #ifdef BIT3 || !BIT3_Initialise(argc, argv) #endif #ifdef NTSC_FILTER || !FILTER_NTSC_Initialise(argc, argv) #endif #if SUPPORTS_CHANGE_VIDEOMODE || !VIDEOMODE_Initialise(argc, argv) #endif #ifndef DONT_DISPLAY /* Platform Specific Initialisation */ || !PLATFORM_Initialise(argc, argv) #endif #if !defined(BASIC) && !defined(CURSES_BASIC) || !Screen_Initialise(argc, argv) #endif /* Initialise Custom Chips */ || !ANTIC_Initialise(argc, argv) || !GTIA_Initialise(argc, argv) || !PIA_Initialise(argc, argv) || !POKEY_Initialise(argc, argv) ) { Atari800_ErrExit(); return FALSE; } #ifndef __PLUS if (help_only) { Atari800_ErrExit(); return FALSE; } #if SUPPORTS_CHANGE_VIDEOMODE #ifndef DONT_DISPLAY if (!VIDEOMODE_InitialiseDisplay()) { Atari800_ErrExit(); return FALSE; } #endif #endif /* Configure Atari System */ Atari800_InitialiseMachine(); #else /* __PLUS */ if (!InitialiseMachine()) { #ifndef _WX_ if (bUpdateRegistry) WriteAtari800Registry(); #endif return FALSE; } #endif /* __PLUS */ /* Auto-start files left on the command line */ j = 1; /* diskno */ for (i = 1; i < *argc; i++) { if (j > 8) { /* The remaining arguments are not necessary disk images, but ignore them... */ Log_print("Too many disk image filenames on the command line (max. 8)."); break; } switch (AFILE_OpenFile(argv[i], i == 1, j, FALSE)) { case AFILE_ERROR: Log_print("Error opening \"%s\"", argv[i]); break; case AFILE_ATR: case AFILE_XFD: case AFILE_ATR_GZ: case AFILE_XFD_GZ: case AFILE_DCM: case AFILE_PRO: j++; break; default: break; } } /* Install requested ROM cartridge */ if (CARTRIDGE_main.type == CARTRIDGE_UNKNOWN) { #ifdef BASIC Log_print("Raw cartridge images not supported in BASIC version!"); #else /* BASIC */ UI_is_active = TRUE; CARTRIDGE_SetType(&CARTRIDGE_main, UI_SelectCartType(CARTRIDGE_main.size)); UI_is_active = FALSE; #endif /* BASIC */ } /* Install requested second ROM cartridge, if first is SpartaX */ if (CARTRIDGE_piggyback.type == CARTRIDGE_UNKNOWN) { #ifdef BASIC Log_print("Raw cartridge images not supported in BASIC version!"); #else /* BASIC */ UI_is_active = TRUE; CARTRIDGE_SetType(&CARTRIDGE_piggyback, UI_SelectCartType(CARTRIDGE_piggyback.size)); UI_is_active = FALSE; #endif /* BASIC */ } #ifdef AF80 /* Insert Austin Franklin 80 column cartridge */ if (AF80_enabled) { AF80_InsertRightCartridge(); } #endif /* Load Atari executable, if any */ if (run_direct != NULL) BINLOAD_Loader(run_direct); #ifndef BASIC /* Load state file */ if (state_file != NULL) { if (StateSav_ReadAtariState(state_file, "rb")) /* Don't press Start nor Option */ GTIA_consol_override = 0; } #endif #ifdef HAVE_SIGNAL /* Install CTRL-C Handler */ signal(SIGINT, sigint_handler); #endif #ifdef __PLUS #ifndef _WX_ /* Update the Registry if any parameters were specified */ if (bUpdateRegistry) WriteAtari800Registry(); Timer_Start(FALSE); #endif /* _WX_ */ g_ulAtariState &= ~ATARI_UNINITIALIZED; #endif /* __PLUS */ #ifdef BENCHMARK benchmark_start_time = Util_time(); #endif #if defined (SOUND) && defined(SOUND_THIN_API) if (Sound_enabled) { /* Up to this point the Sound_enabled flag indicated that we _want_ to enable sound. From now on, the flag will indicate whether audio output is enabled and working. So, since the sound output was not yet initiated, we set the flag accordingly. */ Sound_enabled = FALSE; /* Don't worry, Sound_Setup() will set Sound_enabled back to TRUE if it opens audio output successfully. But Sound_Setup() relies on the flag being set if and only if audio output is active. */ if (Sound_Setup()) /* Start sound if opening audio output was successful. */ Sound_Continue(); } #endif /* defined (SOUND) && defined(SOUND_THIN_API) */ return TRUE; } UNALIGNED_STAT_DEF(Screen_atari_write_long_stat) UNALIGNED_STAT_DEF(pm_scanline_read_long_stat) UNALIGNED_STAT_DEF(memory_read_word_stat) UNALIGNED_STAT_DEF(memory_write_word_stat) UNALIGNED_STAT_DEF(memory_read_aligned_word_stat) UNALIGNED_STAT_DEF(memory_write_aligned_word_stat) int Atari800_Exit(int run_monitor) { int restart; #ifdef __PLUS if (CPU_cim_encountered) g_ulAtariState |= ATARI_CRASHED; #endif #ifdef STAT_UNALIGNED_WORDS printf("(ptr&7) Screen_atari pm_scanline _____ memory ______ memory (aligned addr)\n"); printf(" 32-bit W 32-bit R 16-bit R 16-bit W 16-bit R 16-bit W\n"); { unsigned int sums[6] = {0, 0, 0, 0, 0, 0}; int i; for (i = 0; i < 8; i++) { printf("%6d%12u%14u%11u%11u%11u%11u\n", i, Screen_atari_write_long_stat[i], pm_scanline_read_long_stat[i], memory_read_word_stat[i], memory_write_word_stat[i], memory_read_aligned_word_stat[i], memory_write_aligned_word_stat[i]); sums[0] += Screen_atari_write_long_stat[i]; sums[1] += pm_scanline_read_long_stat[i]; sums[2] += memory_read_word_stat[i]; sums[3] += memory_write_word_stat[i]; sums[4] += memory_read_aligned_word_stat[i]; sums[5] += memory_write_aligned_word_stat[i]; } printf("total:%12u%14u%11u%11u%11u%11u\n", sums[0], sums[1], sums[2], sums[3], sums[4], sums[5]); } #endif /* STAT_UNALIGNED_WORDS */ restart = PLATFORM_Exit(run_monitor); #ifdef HAVE_SIGNAL /* If a user pressed Ctrl+C in the monitor, avoid immediate return to it. */ sigint_flag = FALSE; #endif /* HAVE_SIGNAL */ #ifndef __PLUS if (!restart) { /* We'd better save the configuration before calling the *_Exit() functions - there's a danger that they might change some emulator settings. */ if (CFG_save_on_exit) CFG_WriteConfig(); /* Cleanup functions, in reverse order as the init functions in Atari800_Initialise(). */ #ifdef SOUND Sound_Exit(); #endif #if SUPPORTS_CHANGE_VIDEOMODE VIDEOMODE_Exit(); #endif #ifdef AF80 AF80_Exit(); #endif #ifdef BIT3 BIT3_Exit(); #endif #ifndef BASIC INPUT_Exit(); /* finish event recording */ #endif PBI_Exit(); CASSETTE_Exit(); /* Finish writing to the cassette file */ CARTRIDGE_Exit(); SIO_Exit(); /* umount disks, so temporary files are deleted */ #ifdef IDE IDE_Exit(); #endif Devices_Exit(); #ifdef R_IO_DEVICE RDevice_Exit(); /* R: Device cleanup */ #endif #ifdef SOUND SndSave_CloseSoundFile(); #endif MONITOR_Exit(); #ifdef SDL SDL_INIT_Exit(); #endif /* SDL */ } #endif /* __PLUS */ return restart; } void Atari800_ErrExit(void) { CFG_save_on_exit = FALSE; /* avoid saving the config */ Atari800_Exit(FALSE); } #ifndef __PLUS static void autoframeskip(double curtime, double lasttime) { static int afs_lastframe = 0, afs_discard = 0; static double afs_lasttime = 0.0, afs_sleeptime = 0.0; double afs_speedpct, afs_sleeppct, afs_ataritime, afs_realtime; if (lasttime - curtime > 0) afs_sleeptime += lasttime - curtime; if (curtime - afs_lasttime > 0.5) { afs_ataritime = ((double) (Atari800_nframes - afs_lastframe)) / ((double) (Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC)); afs_realtime = curtime - afs_lasttime; afs_speedpct = 100.0 * afs_ataritime / afs_realtime; afs_sleeppct = 100.0 * afs_sleeptime / afs_realtime; if (afs_discard < 3 && (afs_realtime > 2.0 * afs_ataritime)) { afs_discard++; } else { afs_discard = 0; if (afs_speedpct < 90.0) { if (Atari800_refresh_rate < 4) Atari800_refresh_rate++; } else { if (afs_sleeppct > 20.0 && Atari800_refresh_rate > 1) Atari800_refresh_rate--; } } afs_sleeptime = 0.0; afs_lastframe = Atari800_nframes; afs_lasttime = Util_time(); } } //LIBRETRO HACK #ifdef __LIBRETRO__ #include "libretro-core.h" #endif void Atari800_Sync(void) { static double lasttime = 0; double deltatime = 1.0 / ((Atari800_tv_mode == Atari800_TV_PAL) ? Atari800_FPS_PAL : Atari800_FPS_NTSC); double curtime; #ifdef SYNCHRONIZED_SOUND deltatime *= Sound_AdjustSpeed(); #endif #ifdef ALTERNATE_SYNC_WITH_HOST if (! UI_is_active) deltatime *= Atari800_refresh_rate; #endif lasttime += deltatime; curtime = Util_time(); if (Atari800_auto_frameskip) autoframeskip(curtime, lasttime); Util_sleep(lasttime - curtime); curtime = Util_time(); if ((lasttime + deltatime) < curtime) lasttime = curtime; //LIBRETRO HACK #ifdef __LIBRETRO__ co_switch(mainThread); #endif } #if defined(BASIC) || defined(VERY_SLOW) || defined(CURSES_BASIC) static int scanlines_to_dl; /* steal cycles and generate DLI */ static void basic_antic_scanline(void) { static UBYTE IR = 0; static const UBYTE mode_scanlines[16] = { 0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1 }; static const UBYTE mode_bytes[16] = { 0, 0, 40, 40, 40, 40, 20, 20, 10, 10, 20, 20, 20, 40, 40, 40 }; static const UBYTE font40_cycles[4] = { 0, 32, 40, 47 }; static const UBYTE font20_cycles[4] = { 0, 16, 20, 24 }; #ifdef CURSES_BASIC static int scanlines_to_curses_display = 0; static UWORD screenaddr = 0; static UWORD newscreenaddr = 0; #endif int bytes = 0; if (--scanlines_to_dl <= 0) { if (ANTIC_DMACTL & 0x20) { IR = ANTIC_GetDLByte(&ANTIC_dlist); ANTIC_xpos++; } else IR &= 0x7f; /* repeat last instruction, but don't generate DLI */ switch (IR & 0xf) { case 0: scanlines_to_dl = ((IR >> 4) & 7) + 1; break; case 1: if (ANTIC_DMACTL & 0x20) { ANTIC_dlist = ANTIC_GetDLWord(&ANTIC_dlist); ANTIC_xpos += 2; } scanlines_to_dl = (IR & 0x40) ? 1024 /* no more DL in this frame */ : 1; break; default: if (IR & 0x40 && ANTIC_DMACTL & 0x20) { #ifdef CURSES_BASIC screenaddr = #endif ANTIC_GetDLWord(&ANTIC_dlist); ANTIC_xpos += 2; } /* can't steal cycles now, because DLI must come first */ /* just an approximation: doesn't check HSCROL */ switch (ANTIC_DMACTL & 3) { case 1: bytes = mode_bytes[IR & 0xf] * 8 / 10; break; case 2: bytes = mode_bytes[IR & 0xf]; break; case 3: bytes = mode_bytes[IR & 0xf] * 12 / 10; break; default: break; } #ifdef CURSES_BASIC /* Normally, we would call curses_display_line here, and not use scanlines_to_curses_display at all. That would however cause incorrect color of the "MEMORY" menu item in Self Test - it isn't set properly in the first scanline. We therefore postpone curses_display_line call to the next scanline. */ scanlines_to_curses_display = 2; newscreenaddr = screenaddr + bytes; #endif /* just an approximation: doesn't check VSCROL */ scanlines_to_dl = mode_scanlines[IR & 0xf]; break; } } if (scanlines_to_dl == 1 && (IR & 0x80)) { CPU_GO(ANTIC_NMIST_C); ANTIC_NMIST = 0x9f; if (ANTIC_NMIEN & 0x80) { CPU_GO(ANTIC_NMI_C); CPU_NMI(); } } #ifdef CURSES_BASIC if (--scanlines_to_curses_display == 0) { curses_display_line(IR & 0xf, MEMORY_mem + screenaddr); /* 4k wrap */ if (((screenaddr ^ newscreenaddr) & 0x1000) != 0) screenaddr = newscreenaddr - 0x1000; else screenaddr = newscreenaddr; } #endif ANTIC_xpos += bytes; /* steal cycles in font modes */ switch (IR & 0xf) { case 2: case 3: case 4: case 5: ANTIC_xpos += font40_cycles[ANTIC_DMACTL & 3]; break; case 6: case 7: ANTIC_xpos += font20_cycles[ANTIC_DMACTL & 3]; break; default: break; } } #define BASIC_LINE CPU_GO(ANTIC_LINE_C); ANTIC_xpos -= ANTIC_LINE_C - ANTIC_DMAR; ANTIC_screenline_cpu_clock += ANTIC_LINE_C; ANTIC_ypos++ static void basic_frame(void) { /* scanlines 0 - 7 */ ANTIC_ypos = 0; do { POKEY_Scanline(); /* check and generate IRQ */ BASIC_LINE; } while (ANTIC_ypos < 8); scanlines_to_dl = 1; /* scanlines 8 - 247 */ do { POKEY_Scanline(); /* check and generate IRQ */ basic_antic_scanline(); BASIC_LINE; } while (ANTIC_ypos < 248); /* scanline 248 */ POKEY_Scanline(); /* check and generate IRQ */ CPU_GO(ANTIC_NMIST_C); ANTIC_NMIST = 0x5f; /* Set VBLANK */ if (ANTIC_NMIEN & 0x40) { CPU_GO(ANTIC_NMI_C); CPU_NMI(); } BASIC_LINE; /* scanlines 249 - 261(311) */ do { POKEY_Scanline(); /* check and generate IRQ */ BASIC_LINE; } while (ANTIC_ypos < Atari800_tv_mode); } #endif /* defined(BASIC) || defined(VERY_SLOW) || defined(CURSES_BASIC) */ void Atari800_Frame(void) { #ifndef BASIC static int refresh_counter = 0; #ifdef HAVE_SIGNAL if (sigint_flag) { sigint_flag = FALSE; INPUT_key_code = AKEY_UI; UI_alt_function = UI_MENU_MONITOR; } #endif /* HAVE_SIGNAL */ switch (INPUT_key_code) { case AKEY_COLDSTART: Atari800_Coldstart(); break; case AKEY_WARMSTART: Atari800_Warmstart(); break; case AKEY_EXIT: Atari800_Exit(FALSE); exit(0); case AKEY_TURBO: Atari800_turbo = !Atari800_turbo; break; case AKEY_UI: #ifdef SOUND Sound_Pause(); #endif UI_Run(); #ifdef SOUND Sound_Continue(); #endif break; #ifndef CURSES_BASIC case AKEY_SCREENSHOT: Screen_SaveNextScreenshot(FALSE); break; case AKEY_SCREENSHOT_INTERLACE: Screen_SaveNextScreenshot(TRUE); break; #endif /* CURSES_BASIC */ case AKEY_PBI_BB_MENU: #ifdef PBI_BB PBI_BB_Menu(); #endif break; default: break; } #endif /* BASIC */ #ifdef PBI_BB PBI_BB_Frame(); /* just to make the menu key go up automatically */ #endif #if defined(PBI_XLD) || defined (VOICEBOX) VOTRAXSND_Frame(); /* for the Votrax */ #endif Devices_Frame(); #ifndef BASIC INPUT_Frame(); #endif GTIA_Frame(); #ifdef BASIC basic_frame(); #else /* BASIC */ if (++refresh_counter >= Atari800_refresh_rate) { refresh_counter = 0; #ifdef USE_CURSES curses_clear_screen(); #endif #ifdef CURSES_BASIC basic_frame(); #else ANTIC_Frame(TRUE); INPUT_DrawMousePointer(); Screen_DrawAtariSpeed(Util_time()); Screen_DrawDiskLED(); Screen_Draw1200LED(); #endif /* CURSES_BASIC */ #ifdef DONT_DISPLAY Atari800_display_screen = FALSE; #else Atari800_display_screen = TRUE; #endif /* DONT_DISPLAY */ } else { #if defined(VERY_SLOW) || defined(CURSES_BASIC) basic_frame(); #else ANTIC_Frame(Atari800_collisions_in_skipped_frames); #endif Atari800_display_screen = FALSE; } #endif /* BASIC */ POKEY_Frame(); #ifdef SOUND Sound_Update(); #endif Atari800_nframes++; #ifdef BENCHMARK if (Atari800_nframes >= BENCHMARK) { double benchmark_time = Util_time() - benchmark_start_time; Atari800_ErrExit(); printf("%d frames emulated in %.2f seconds\n", BENCHMARK, benchmark_time); exit(0); } #else #ifdef ALTERNATE_SYNC_WITH_HOST if (refresh_counter == 0) #endif if (Atari800_turbo) { /* No need to draw Atari frames with frequency higher than display refresh rate. */ static double last_display_screen_time = 0.0; static double const limit = 1.0 / 60.0; /* refresh every 1/60 s */ /* TODO Actually sync the limit with the display refresh rate. */ double cur_time = Util_time(); if (cur_time - last_display_screen_time > limit) last_display_screen_time = cur_time; else Atari800_display_screen = FALSE; } else Atari800_Sync(); #endif /* BENCHMARK */ } #endif /* __PLUS */ #ifndef BASIC void Atari800_StateSave(void) { UBYTE temp = Atari800_tv_mode == Atari800_TV_PAL; StateSav_SaveUBYTE(&temp, 1); temp = Atari800_machine_type; StateSav_SaveUBYTE(&temp, 1); if (Atari800_machine_type == Atari800_MACHINE_XLXE) { temp = Atari800_builtin_basic; StateSav_SaveUBYTE(&temp, 1); temp = Atari800_keyboard_leds; StateSav_SaveUBYTE(&temp, 1); temp = Atari800_f_keys; StateSav_SaveUBYTE(&temp, 1); temp = Atari800_jumper; StateSav_SaveUBYTE(&temp, 1); temp = Atari800_builtin_game; StateSav_SaveUBYTE(&temp, 1); temp = Atari800_keyboard_detached; StateSav_SaveUBYTE(&temp, 1); } } void Atari800_StateRead(UBYTE version) { if (version >= 7) { UBYTE temp; StateSav_ReadUBYTE(&temp, 1); Atari800_SetTVMode(temp ? Atari800_TV_PAL : Atari800_TV_NTSC); StateSav_ReadUBYTE(&temp, 1); if (temp < 0 || temp >= Atari800_MACHINE_SIZE) { temp = Atari800_MACHINE_XLXE; Log_print("Warning: Bad machine type read in from state save, defaulting to XL/XE"); } Atari800_SetMachineType(temp); if (Atari800_machine_type == Atari800_MACHINE_XLXE) { StateSav_ReadUBYTE(&temp, 1); Atari800_builtin_basic = temp != 0; StateSav_ReadUBYTE(&temp, 1); Atari800_keyboard_leds = temp != 0; StateSav_ReadUBYTE(&temp, 1); Atari800_f_keys = temp != 0; StateSav_ReadUBYTE(&temp, 1); Atari800_jumper = temp != 0; Atari800_UpdateJumper(); StateSav_ReadUBYTE(&temp, 1); Atari800_builtin_game = temp != 0; StateSav_ReadUBYTE(&temp, 1); Atari800_keyboard_detached = temp != 0; Atari800_UpdateKeyboardDetached(); } } else { /* savestate from version 2.2.1 or earlier */ int new_tv_mode; /* these are all for compatibility with previous versions */ UBYTE temp; int default_tv_mode; int os; int default_system; int pil_on; StateSav_ReadUBYTE(&temp, 1); new_tv_mode = (temp == 0) ? Atari800_TV_PAL : Atari800_TV_NTSC; Atari800_SetTVMode(new_tv_mode); StateSav_ReadUBYTE(&temp, 1); StateSav_ReadINT(&os, 1); switch (temp) { case 0: Atari800_machine_type = Atari800_MACHINE_800; MEMORY_ram_size = 48; break; case 1: Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 64; break; case 2: Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 128; break; case 3: Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = MEMORY_RAM_320_COMPY_SHOP; break; case 4: Atari800_machine_type = Atari800_MACHINE_5200; MEMORY_ram_size = 16; break; case 5: Atari800_machine_type = Atari800_MACHINE_800; MEMORY_ram_size = 16; break; case 6: Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 16; break; case 7: Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 576; break; case 8: Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 1088; break; case 9: Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 192; break; default: Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 64; Log_print("Warning: Bad machine type read in from state save, defaulting to 800 XL"); break; } StateSav_ReadINT(&pil_on, 1); StateSav_ReadINT(&default_tv_mode, 1); StateSav_ReadINT(&default_system, 1); Atari800_SetMachineType(Atari800_machine_type); } load_roms(); /* XXX: what about patches? */ } #endif void Atari800_SetTVMode(int mode) { if (mode != Atari800_tv_mode) { Atari800_tv_mode = mode; #if !defined(BASIC) && !defined(CURSES_BASIC) Colours_SetVideoSystem(mode); ARTIFACT_SetTVMode(mode); #endif #if SUPPORTS_CHANGE_VIDEOMODE VIDEOMODE_SetVideoSystem(mode); #endif #ifdef SOUND #ifdef SOUND_THIN_API if (Sound_enabled) POKEYSND_Init(POKEYSND_FREQ_17_EXACT, Sound_out.freq, Sound_out.channels, Sound_out.sample_size == 2 ? POKEYSND_BIT16 : 0); #elif defined(SUPPORTS_SOUND_REINIT) Sound_Reinit(); #endif /* defined(SUPPORTS_SOUND_REINIT) */ #endif /* SOUND */ #if defined(DIRECTX) SetTVModeMenuItem(mode); #endif } }