/* * atari.c - main high-level routines * * Copyright (c) 1995-1998 David Firth * Copyright (c) 1998-2008 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 */ #include "afile.h" #include "config.h" #include #include #include #ifdef HAVE_SIGNAL_H #include #endif #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # elif defined(HAVE_TIME_H) # include # endif #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef WIN32 #include #endif #ifdef __EMX__ #define INCL_DOS #include #endif #ifdef __BEOS__ #include #endif #ifdef HAVE_LIBZ #include #endif #ifdef R_SERIAL #include #endif #include "akey.h" #include "antic.h" #include "atari.h" #include "binload.h" #include "cartridge.h" #include "cassette.h" #include "cfg.h" #include "cpu.h" #include "devices.h" #include "esc.h" #include "gtia.h" #include "input.h" #include "log.h" #include "memory.h" #include "monitor.h" #include "pia.h" #include "platform.h" #include "pokey.h" #include "rtime.h" #include "pbi.h" #include "sio.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 #ifdef PBI_XLD #include "pbi_xld.h" #endif #ifdef XEP80_EMULATION #include "xep80.h" #endif #ifdef NTSC_FILTER #include "atari_ntsc.h" #endif int Atari800_machine_type = Atari800_MACHINE_XLXE; int Atari800_tv_mode = Atari800_TV_PAL; int Atari800_disable_basic = TRUE; int verbose = FALSE; int Atari800_display_screen = FALSE; int Atari800_nframes = 0; int Atari800_refresh_rate = 1; int Atari800_collisions_in_skipped_frames = FALSE; #ifdef BENCHMARK static double benchmark_start_time; static double Atari_time(void); #endif int emuos_mode = 1; /* 0 = never use EmuOS, 1 = use EmuOS if real OS not available, 2 = always use EmuOS */ #ifdef HAVE_SIGNAL static RETSIGTYPE sigint_handler(int num) { int restart; restart = Atari800_Exit(TRUE); if (restart) { signal(SIGINT, sigint_handler); return; } exit(0); } #endif void Atari800_Warmstart(void) { if (Atari800_machine_type == Atari800_MACHINE_OSA || Atari800_machine_type == Atari800_MACHINE_OSB) { /* A real Axlon homebanks on reset */ /* XXX: what does Mosaic do? */ if (MEMORY_axlon_enabled) 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_Start(); /* 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_index = 2; GTIA_consol_table[2] = 0x0f; if (Atari800_disable_basic && !BINLOAD_loading_basic) { /* hold Option during reboot */ GTIA_consol_table[2] &= ~INPUT_CONSOL_OPTION; } if (CASSETTE_hold_start) { /* hold Start during reboot */ GTIA_consol_table[2] &= ~INPUT_CONSOL_START; } GTIA_consol_table[1] = GTIA_consol_table[2]; } 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; } #include "emuos.h" #define COPY_EMUOS(padding) do { \ memset(MEMORY_os, 0, padding); \ memcpy(MEMORY_os + (padding), emuos_h, 0x2000); \ } while (0) static int load_roms(void) { switch (Atari800_machine_type) { case Atari800_MACHINE_OSA: if (emuos_mode == 2) COPY_EMUOS(0x0800); else if (!Atari800_LoadImage(CFG_osa_filename, MEMORY_os, 0x2800)) { if (emuos_mode == 1) COPY_EMUOS(0x0800); else return FALSE; } else MEMORY_have_basic = Atari800_LoadImage(CFG_basic_filename, MEMORY_basic, 0x2000); break; case Atari800_MACHINE_OSB: if (emuos_mode == 2) COPY_EMUOS(0x0800); else if (!Atari800_LoadImage(CFG_osb_filename, MEMORY_os, 0x2800)) { if (emuos_mode == 1) COPY_EMUOS(0x0800); else return FALSE; } else MEMORY_have_basic = Atari800_LoadImage(CFG_basic_filename, MEMORY_basic, 0x2000); break; case Atari800_MACHINE_XLXE: if (emuos_mode == 2) COPY_EMUOS(0x2000); else if (!Atari800_LoadImage(CFG_xlxe_filename, MEMORY_os, 0x4000)) { if (emuos_mode == 1) COPY_EMUOS(0x2000); else return FALSE; } else { /* if you really don't want built-in BASIC */ if (!strcmp(CFG_basic_filename,"none")) memset(MEMORY_basic, 0, 0x2000); else if (!Atari800_LoadImage(CFG_basic_filename, MEMORY_basic, 0x2000)) return FALSE; } MEMORY_xe_bank = 0; break; case Atari800_MACHINE_5200: if (!Atari800_LoadImage(CFG_5200_filename, MEMORY_os, 0x800)) return FALSE; break; } return TRUE; } int Atari800_InitialiseMachine(void) { #if !defined(BASIC) && !defined(CURSES_BASIC) Colours_InitialiseMachine(); #endif ESC_ClearAll(); if (!load_roms()) return FALSE; MEMORY_InitialiseMachine(); Devices_UpdatePatches(); return TRUE; } int Atari800_Initialise(int *argc, char *argv[]) { int i, j; const char *rom_filename = NULL; 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_type; /* It is necessary because of the CARTRIDGE_Start (there must not be the registry-read value available at startup) */ CARTRIDGE_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_ */ #else /* __PLUS */ const char *rtconfig_filename = NULL; int got_config; int help_only = FALSE; 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; } got_config = CFG_LoadConfig(rtconfig_filename); /* 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) */ CFG_FindROMImages("", TRUE); /* current directory */ #if defined(unix) || defined(__unix__) || defined(__linux__) CFG_FindROMImages("/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); CFG_FindROMImages(atari800_exe_dir, TRUE); /* "rom" and "ROM" subdirectories of this directory */ Util_catpath(atari800_exe_rom_dir, atari800_exe_dir, "rom"); CFG_FindROMImages(atari800_exe_rom_dir, TRUE); /* skip "ROM" on systems that are known to be case-insensitive */ #if !defined(DJGPP) && !defined(WIN32) Util_catpath(atari800_exe_rom_dir, atari800_exe_dir, "ROM"); CFG_FindROMImages(atari800_exe_rom_dir, TRUE); #endif } /* finally if nothing is found, set some defaults to make the configuration file easier to edit */ if (Util_filenamenotset(CFG_osa_filename)) strcpy(CFG_osa_filename, "atariosa.rom"); if (Util_filenamenotset(CFG_osb_filename)) strcpy(CFG_osb_filename, "atariosb.rom"); if (Util_filenamenotset(CFG_xlxe_filename)) strcpy(CFG_xlxe_filename, "atarixl.rom"); if (Util_filenamenotset(CFG_5200_filename)) strcpy(CFG_5200_filename, "5200.rom"); if (Util_filenamenotset(CFG_basic_filename)) strcpy(CFG_basic_filename, "ataribas.rom"); /* 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) { if (Atari800_machine_type != Atari800_MACHINE_OSA) { Atari800_machine_type = Atari800_MACHINE_OSB; MEMORY_ram_size = 48; } } else if (strcmp(argv[i], "-xl") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 64; } else if (strcmp(argv[i], "-xe") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 128; } else if (strcmp(argv[i], "-320xe") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = MEMORY_RAM_320_COMPY_SHOP; } else if (strcmp(argv[i], "-rambo") == 0) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = MEMORY_RAM_320_RAMBO; } else if (strcmp(argv[i], "-5200") == 0) { Atari800_machine_type = Atari800_MACHINE_5200; MEMORY_ram_size = 16; } 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], "-a") == 0) { Atari800_machine_type = Atari800_MACHINE_OSA; MEMORY_ram_size = 48; } else if (strcmp(argv[i], "-b") == 0) { Atari800_machine_type = Atari800_MACHINE_OSB; MEMORY_ram_size = 48; } else if (strcmp(argv[i], "-emuos") == 0) emuos_mode = 2; else if (strcmp(argv[i], "-c") == 0) { if (MEMORY_ram_size == 48) 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 { /* 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], "-osa_rom") == 0) { if (i_a) Util_strlcpy(CFG_osa_filename, argv[++i], sizeof(CFG_osa_filename)); 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], "-osb_rom") == 0) { if (i_a) Util_strlcpy(CFG_osb_filename, argv[++i], sizeof(CFG_osb_filename)); else a_m = TRUE; } else if (strcmp(argv[i], "-xlxe_rom") == 0) { if (i_a) Util_strlcpy(CFG_xlxe_filename, argv[++i], sizeof(CFG_xlxe_filename)); else a_m = TRUE; } else if (strcmp(argv[i], "-5200_rom") == 0) { if (i_a) Util_strlcpy(CFG_5200_filename, argv[++i], sizeof(CFG_5200_filename)); else a_m = TRUE; } else if (strcmp(argv[i], "-basic_rom") == 0) { if (i_a) Util_strlcpy(CFG_basic_filename, argv[++i], sizeof(CFG_basic_filename)); else a_m = TRUE; } else if (strcmp(argv[i], "-cart") == 0) { if (i_a) rom_filename = argv[++i]; else a_m = TRUE; } else if (strcmp(argv[i], "-run") == 0) { if (i_a) run_direct = argv[++i]; else a_m = TRUE; } else if (strcmp(argv[i], "-mosaic") == 0) { if (i_a) { int total_ram = Util_sscandec(argv[++i]); MEMORY_mosaic_enabled = TRUE; MEMORY_mosaic_maxbank = (total_ram - 48)/4 - 1; if (((total_ram - 48) % 4 != 0) || (MEMORY_mosaic_maxbank > 0x3e) || (MEMORY_mosaic_maxbank < 0)) { Log_print("Invalid Mosaic total RAM size"); return FALSE; } if (MEMORY_axlon_enabled) { Log_print("Axlon and Mosaic can not both be enabled, because they are incompatible"); return FALSE; } } else a_m = TRUE; } else if (strcmp(argv[i], "-axlon") == 0) { if (i_a) { int total_ram = Util_sscandec(argv[++i]); int banks = ((total_ram) - 32) / 16; MEMORY_axlon_enabled = TRUE; if (((total_ram - 32) % 16 != 0) || ((banks != 8) && (banks != 16) && (banks != 32) && (banks != 64) && (banks != 128) && (banks != 256))) { Log_print("Invalid Axlon total RAM size"); return FALSE; } if (MEMORY_mosaic_enabled) { Log_print("Axlon and Mosaic can not both be enabled, because they are incompatible"); return FALSE; } MEMORY_axlon_bankmask = banks - 1; } else a_m = TRUE; } #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; } #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-atari Emulate Atari 800"); 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)"); 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-osa_rom Load OS A ROM from file"); Log_print("\t-osb_rom Load OS B ROM from file"); Log_print("\t-xlxe_rom Load XL/XE ROM from file"); Log_print("\t-5200_rom Load 5200 ROM from file"); Log_print("\t-basic_rom Load BASIC ROM from file"); Log_print("\t-cart Install cartridge (raw or CART format)"); 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-a Use OS A"); Log_print("\t-b Use OS B"); 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"); #ifdef R_IO_DEVICE Log_print("\t-rdevice [] Enable R: emulation (using serial device )"); #endif 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; } } } *argc = j; if ( #if !defined(BASIC) && !defined(CURSES_BASIC) !Colours_Initialise(argc, argv) || #endif !Devices_Initialise(argc, argv) || !RTIME_Initialise(argc, argv) || !SIO_Initialise (argc, argv) || !CASSETTE_Initialise(argc, argv) || !PBI_Initialise(argc,argv) #ifndef BASIC || !INPUT_Initialise(argc, argv) #endif #ifdef XEP80_EMULATION || !XEP80_Initialise(argc, argv) #endif #ifdef NTSC_FILTER || !atari_ntsc_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) ) return FALSE; #ifndef __PLUS if (help_only) { Atari800_Exit(FALSE); return FALSE; } /* 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 (rom_filename) { int r = CARTRIDGE_Insert(rom_filename); if (r < 0) { Log_print("Error inserting cartridge \"%s\": %s", rom_filename, r == CARTRIDGE_CANT_OPEN ? "Can't open file" : r == CARTRIDGE_BAD_FORMAT ? "Bad format" : r == CARTRIDGE_BAD_CHECKSUM ? "Bad checksum" : "Unknown error"); } if (r > 0) { #ifdef BASIC Log_print("Raw cartridge images not supported in BASIC version!"); #else /* BASIC */ #ifndef __PLUS UI_is_active = TRUE; CARTRIDGE_type = UI_SelectCartType(r); UI_is_active = FALSE; #else /* __PLUS */ CARTRIDGE_type = (CARTRIDGE_NONE == nCartType ? UI_SelectCartType(r) : nCartType); #endif /* __PLUS */ CARTRIDGE_Start(); #endif /* BASIC */ } #ifndef __PLUS if (CARTRIDGE_type != CARTRIDGE_NONE) { int for5200 = CARTRIDGE_IsFor5200(CARTRIDGE_type); if (for5200 && Atari800_machine_type != Atari800_MACHINE_5200) { Atari800_machine_type = Atari800_MACHINE_5200; MEMORY_ram_size = 16; Atari800_InitialiseMachine(); } else if (!for5200 && Atari800_machine_type == Atari800_MACHINE_5200) { Atari800_machine_type = Atari800_MACHINE_XLXE; MEMORY_ram_size = 64; Atari800_InitialiseMachine(); } } #endif /* __PLUS */ } /* 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 Option */ GTIA_consol_table[1] = GTIA_consol_table[2] = 0x0f; } #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 = Atari_time(); #endif 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); #ifndef __PLUS if (!restart) { SIO_Exit(); /* umount disks, so temporary files are deleted */ #ifndef BASIC INPUT_Exit(); /* finish event recording */ #endif #ifdef R_IO_DEVICE RDevice_Exit(); /* R: Device cleanup */ #endif #ifdef SOUND SndSave_CloseSoundFile(); #endif } #endif /* __PLUS */ return restart; } #ifndef __PLUS #ifdef PS2 double Atari_time(void); void Atari_sleep(double s); #else /* PS2 */ static double Atari_time(void) { #ifdef WIN32 return GetTickCount() * 1e-3; #elif defined(DJGPP) /* DJGPP has gettimeofday, but it's not more accurate than uclock */ return uclock() * (1.0 / UCLOCKS_PER_SEC); #elif defined(HAVE_GETTIMEOFDAY) struct timeval tp; gettimeofday(&tp, NULL); return tp.tv_sec + 1e-6 * tp.tv_usec; #elif defined(HAVE_UCLOCK) return uclock() * (1.0 / UCLOCKS_PER_SEC); #elif defined(HAVE_CLOCK) return clock() * (1.0 / CLK_TCK); #else #error No function found for Atari_time() #endif } /* FIXME: Ports should use SUPPORTS_PLATFORM_SLEEP and SUPPORTS_PLATFORM_TIME */ /* and not this mess */ #ifndef SUPPORTS_PLATFORM_SLEEP static void Atari_sleep(double s) { if (s > 0) { #ifdef WIN32 Sleep((DWORD) (s * 1e3)); #elif defined(DJGPP) /* DJGPP has usleep and select, but they don't work that good */ /* XXX: find out why */ double curtime = Atari_time(); while ((curtime + s) > Atari_time()); #elif defined(HAVE_NANOSLEEP) struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = s * 1e9; nanosleep(&ts, NULL); #elif defined(HAVE_USLEEP) usleep(s * 1e6); #elif defined(__BEOS__) /* added by Walter Las for BeOS */ snooze(s * 1e6); #elif defined(__EMX__) /* added by Brian Smith for os/2 */ DosSleep(s); #elif defined(HAVE_SELECT) /* linux */ struct timeval tp; tp.tv_sec = 0; tp.tv_usec = s * 1e6; select(1, NULL, NULL, NULL, &tp); #else double curtime = Atari_time(); while ((curtime + s) > Atari_time()); #endif } } #endif /* SUPPORTS_PLATFORM_SLEEP */ #endif /* PS2 */ void Atari800_Sync(void) { static double lasttime = 0; double deltatime = 1.0 / ((Atari800_tv_mode == Atari800_TV_PAL) ? 50 : 60); double curtime; #ifdef ALTERNATE_SYNC_WITH_HOST if (! UI_is_active) deltatime *= Atari800_refresh_rate; #endif lasttime += deltatime; #ifdef SUPPORTS_PLATFORM_SLEEP PLATFORM_Sleep(lasttime - Atari_time()); #else Atari_sleep(lasttime - Atari_time()); #endif curtime = Atari_time(); if ((lasttime + deltatime) < curtime) lasttime = curtime; } #ifdef USE_CURSES void curses_clear_screen(void); #endif #if defined(BASIC) || defined(VERY_SLOW) || defined(CURSES_BASIC) #ifdef CURSES_BASIC void curses_display_line(int anticmode, const UBYTE *screendata); #endif 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; 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_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 #ifdef PBI_XLD PBI_XLD_VFrame(); /* for the Votrax */ #endif Devices_Frame(); #ifndef BASIC INPUT_Frame(); #endif GTIA_Frame(); #ifdef SOUND Sound_Update(); #endif #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(Atari_time()); Screen_DrawDiskLED(); #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(); Atari800_nframes++; #ifdef BENCHMARK if (Atari800_nframes >= BENCHMARK) { double benchmark_time = Atari_time() - benchmark_start_time; Atari800_Exit(FALSE); printf("%d frames emulated in %.2f seconds\n", BENCHMARK, benchmark_time); exit(0); } #else #ifdef ALTERNATE_SYNC_WITH_HOST if (refresh_counter == 0) #endif Atari800_Sync(); #endif /* BENCHMARK */ } #endif /* __PLUS */ #ifndef BASIC void Atari800_StateSave(void) { UBYTE temp; int default_tv_mode; int os = 0; int default_system = 3; int pil_on = FALSE; if (Atari800_tv_mode == Atari800_TV_PAL) { temp = 0; default_tv_mode = 1; } else { temp = 1; default_tv_mode = 2; } StateSav_SaveUBYTE(&temp, 1); switch (Atari800_machine_type) { case Atari800_MACHINE_OSA: temp = MEMORY_ram_size == 16 ? 5 : 0; os = 1; default_system = 1; break; case Atari800_MACHINE_OSB: temp = MEMORY_ram_size == 16 ? 5 : 0; os = 2; default_system = 2; break; case Atari800_MACHINE_XLXE: switch (MEMORY_ram_size) { case 16: temp = 6; default_system = 3; break; case 64: temp = 1; default_system = 3; break; case 128: temp = 2; default_system = 4; break; case 192: temp = 9; default_system = 8; break; case MEMORY_RAM_320_RAMBO: case MEMORY_RAM_320_COMPY_SHOP: temp = 3; default_system = 5; break; case 576: temp = 7; default_system = 6; break; case 1088: temp = 8; default_system = 7; break; } break; case Atari800_MACHINE_5200: temp = 4; default_system = 6; break; } StateSav_SaveUBYTE(&temp, 1); StateSav_SaveINT(&os, 1); StateSav_SaveINT(&pil_on, 1); StateSav_SaveINT(&default_tv_mode, 1); StateSav_SaveINT(&default_system, 1); } void Atari800_StateRead(void) { 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; if (new_tv_mode != Atari800_tv_mode) { Atari800_tv_mode = new_tv_mode; #if !defined(BASIC) && !defined(CURSES_BASIC) Colours_InitialiseMachine(); #endif } StateSav_ReadUBYTE(&temp, 1); StateSav_ReadINT(&os, 1); switch (temp) { case 0: Atari800_machine_type = os == 1 ? Atari800_MACHINE_OSA : Atari800_MACHINE_OSB; 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 = os == 1 ? Atari800_MACHINE_OSA : Atari800_MACHINE_OSB; 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); load_roms(); /* XXX: what about patches? */ } #endif