ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/arch/unix/vsync.c

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.