This is vsync.c in view mode; [Download] [Up]
/* * vsync.c - End-of-frame handling for Unix. * * Written by * Ettore Perazzoli (ettore@comm2000.it) * Teemu Rantanen (tvr@cs.hut.fi) * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * 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., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ /* This does what has to be done at the end of each screen frame (50 times per second on PAL machines). */ #include "vice.h" #ifdef __hpux #define _INCLUDE_HPUX_SOURCE #define _INCLUDE_POSIX_SOURCE #define _INCLUDE_XOPEN_SOURCE #endif #include <sys/time.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #ifdef __hpux #define _INCLUDE_XOPEN_SOURCE #define _XPG2 #include <limits.h> #undef _INCLUDE_XOPEN_SOURCE #undef _XPG2 #else #include <limits.h> #endif #include "vsync.h" #include "ui.h" #include "interrupt.h" #include "video.h" #include "kbdbuf.h" #include "sound.h" #include "resources.h" #include "cmdline.h" #ifdef HAS_JOYSTICK #include "joystick.h" #endif #ifndef SA_RESTART #define SA_RESTART 0 #endif /* ------------------------------------------------------------------------- */ /* Relative speed of the emulation (%). 0 means "don't limit speed". */ static int relative_speed; /* Refresh rate. 0 means "auto". */ static int refresh_rate; /* "Warp mode". If nonzero, attempt to run as fast as possible. */ static int warp_mode_enabled; /* FIXME: This should call `set_timers'. */ static int set_relative_speed(resource_value_t v) { relative_speed = (int) v; return 0; } static int set_refresh_rate(resource_value_t v) { if ((int) v < 0) return -1; refresh_rate = (int) v; return 0; } static int set_warp_mode(resource_value_t v) { warp_mode_enabled = (int) v; sound_set_warp_mode(warp_mode_enabled); return 0; } /* Vsync-related resources. */ static resource_t resources[] = { { "Speed", RES_INTEGER, (resource_value_t) 100, (resource_value_t *) &relative_speed, set_relative_speed }, { "RefreshRate", RES_INTEGER, (resource_value_t) 0, (resource_value_t *) &refresh_rate, set_refresh_rate }, { "WarpMode", RES_INTEGER, (resource_value_t) 0, (resource_value_t *) &warp_mode_enabled, set_warp_mode }, { NULL } }; int vsync_init_resources(void) { return resources_register(resources); } /* ------------------------------------------------------------------------- */ /* Vsync-related command-line options. */ static cmdline_option_t cmdline_options[] = { { "-speed", SET_RESOURCE, 1, NULL, NULL, "Speed", NULL, "<percent>", "Limit emulation speed to specified value" }, { "-refresh", SET_RESOURCE, 1, NULL, NULL, "RefreshRate", NULL, "<value>", "Update every <value> frames (`0' for automatic)" }, { "-warp", SET_RESOURCE, 0, NULL, NULL, "WarpMode", (resource_value_t) 1, NULL, "Enable warp mode" }, { "+warp", SET_RESOURCE, 0, NULL, NULL, "WarpMode", (resource_value_t) 0, NULL, "Disable warp mode" }, { NULL } }; int vsync_init_cmdline_options(void) { return cmdline_register_options(cmdline_options); } /* ------------------------------------------------------------------------- */ /* Maximum number of frames we can skip consecutively when adjusting the refresh rate dynamically. */ #define MAX_SKIPPED_FRAMES 10 /* Number of frames per second on the real machine. */ static double refresh_frequency; /* Number of clock cycles per seconds on the real machine. */ static long cycles_per_sec; /* Function to call at the end of every screen frame. */ static void (*vsync_hook)(void); /* ------------------------------------------------------------------------- */ static volatile int elapsed_frames = 0; static int timer_disabled = 1; static int timer_speed = 0; static int timer_patch = 0; static int timer_ticks; static struct timeval timer_time; static void update_elapsed_frames(int want) { struct timeval now; static int pending; if (timer_disabled) return; if (!want && timer_patch > 0) { timer_patch--; elapsed_frames++; } gettimeofday(&now, NULL); while (now.tv_sec > timer_time.tv_sec || (now.tv_sec == timer_time.tv_sec && now.tv_usec > timer_time.tv_usec)) { if (pending) pending--; else { if (timer_patch < 0) timer_patch++; else elapsed_frames++; } timer_time.tv_usec += timer_ticks; while (timer_time.tv_usec >= 1000000) { timer_time.tv_usec -= 1000000; timer_time.tv_sec++; } } if (want == 1 && !pending) { if (timer_patch < 0) timer_patch++; else elapsed_frames++; pending++; } } static int set_timer_speed(int speed) { if (speed > 0) { gettimeofday(&timer_time, NULL); timer_ticks = ((100.0 / refresh_frequency) * 1000000) / speed; update_elapsed_frames(-1); elapsed_frames = 0; } else { speed = 0; timer_ticks = 0; } timer_speed = speed; timer_disabled = speed ? 0 : 1; return 0; } static void timer_sleep(void) { int old; if (!timer_disabled) { old = elapsed_frames; while (old == elapsed_frames) { update_elapsed_frames(1); if (old == elapsed_frames) usleep(1); } } } static void patch_timer(int patch) { timer_patch += patch; } int vsync_disable_timer(void) { if (!timer_disabled) return set_timer_speed(0); else return 0; } /* ------------------------------------------------------------------------- */ static int speed_eval_suspended = 1; /* This should be called whenever something that has nothing to do with the emulation happens, so that we don't display bogus speed values. */ void suspend_speed_eval(void) { sound_suspend(); speed_eval_suspended = 1; } void vsync_init(double hz, long cycles, void (*hook)(void)) { vsync_hook = hook; refresh_frequency = hz; cycles_per_sec = cycles; suspend_speed_eval(); vsync_disable_timer(); } static CLOCK speed_eval_prev_clk; static void display_speed(int num_frames) { #ifdef HAVE_GETTIMEOFDAY static double prev_time; struct timeval tv; double curr_time; gettimeofday(&tv, NULL); curr_time = (double)tv.tv_sec + ((double)tv.tv_usec) / 1000000.0; if (!speed_eval_suspended) { CLOCK diff_clk; double speed_index; double frame_rate; diff_clk = clk - speed_eval_prev_clk; frame_rate = (double)num_frames / (curr_time - prev_time); speed_index = ((((double)diff_clk / (curr_time - prev_time)) / (double)cycles_per_sec)) * 100.0; ui_display_speed((float)speed_index, (float)frame_rate, warp_mode_enabled); } prev_time = curr_time; speed_eval_prev_clk = clk; speed_eval_suspended = 0; #else /* HAVE_GETTIMEOFDAY */ /* Sorry, no speed evaluation. */ return; #endif /* HAVE_GETTIMEOFDAY */ } void vsync_prevent_clk_overflow(CLOCK sub) { speed_eval_prev_clk -= sub; } /* ------------------------------------------------------------------------- */ /* This is called at the end of each screen frame. It flushes the audio buffer and keeps control of the emulation speed. */ int do_vsync(int been_skipped) { static unsigned short frame_counter = USHRT_MAX; static unsigned short num_skipped_frames; static int skip_counter; int skip_next_frame = 0; vsync_hook(); if (been_skipped) num_skipped_frames++; if (timer_speed != relative_speed) { frame_counter = USHRT_MAX; if (set_timer_speed(relative_speed) < 0) { fprintf(stderr, "Trouble setting timers... giving up.\n"); exit(-1); } } ui_dispatch_events(); if (warp_mode_enabled) { /* "Warp Mode". Just skip as many frames as possible and do not limit the maximum speed at all. */ if (skip_counter < MAX_SKIPPED_FRAMES) { skip_next_frame = 1; skip_counter++; } else { skip_counter = elapsed_frames = 0; } /* sound_flush(0); */ } else if (refresh_rate != 0) { /* Fixed refresh rate. */ update_elapsed_frames(0); if (timer_speed && skip_counter >= elapsed_frames) timer_sleep(); if (skip_counter < refresh_rate - 1) { skip_next_frame = 1; skip_counter++; } else { skip_counter = elapsed_frames = 0; } patch_timer(sound_flush(relative_speed)); } else { /* Dynamically adjusted refresh rate. */ update_elapsed_frames(0); if (skip_counter >= elapsed_frames) { elapsed_frames = -1; timer_sleep(); skip_counter = 0; } else { if (skip_counter < MAX_SKIPPED_FRAMES) { skip_next_frame = 1; skip_counter++; } else { skip_counter = elapsed_frames = 0; } } patch_timer(sound_flush(relative_speed)); } if (frame_counter >= refresh_frequency * 2) { display_speed(frame_counter + 1 - num_skipped_frames); num_skipped_frames = 0; frame_counter = 0; } else frame_counter++; kbd_buf_flush(); #ifdef HAS_JOYSTICK joystick(); #endif return skip_next_frame; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.