/* * ============================================================================ * Title: Speed control * Author: J. Zbiciak * $Id: speed.c,v 1.10 1999/10/10 08:44:30 im14u2c Exp $ * ============================================================================ * SPEED_T -- Speed-control peripheral * SPEED_TK -- Main throttling agent. * SPEED_SET -- Set desired speed as a fraction out of 256. * SPEED_GET -- Query current average running speed, tick rate, etc. * ============================================================================ * Attempts to control speed of emulation. Invoked as a peripheral. * This code will tend to be very platform dependent, I'm guessing. * The initial implementation will be a busy-loop that calls gettimeofday * repeatedly * ============================================================================ */ static const char rcs_id[]="$Id: speed.c,v 1.10 1999/10/10 08:44:30 im14u2c Exp $"; #ifndef macintosh # include #endif #include "config.h" #include "periph/periph.h" #include "gfx/gfx.h" #include "speed.h" #define LATE_TOLERANCE (256) #define MICROSEC_PER_FRAME (16686) #define DELAY_THRESH (4000) /* * ============================================================================ * SPEED_TK -- Main throttling agent. * ============================================================================ */ uint_32 speed_tk(periph_t *p, uint_32 len) { speed_t *speed = (speed_t*)p; uint_32 usec, elapsed, thresh; struct timeval now, then; /* -------------------------------------------------------------------- */ /* The value of 'len' says how long we've gone w/out a tick. We */ /* compare this against the amount of real time that's elapsed, and */ /* act accordingly: */ /* */ /* -- If it appears "no" real time has passed, then we're being */ /* called too often, so adjust our min tick rate outwards. */ /* We don't let this get larger than max_tick, though, which is */ /* set to correspond to 60Hz. */ /* */ /* -- If too little real time has passed, sit in a busy loop and */ /* for real time to catch up to simulation time. */ /* */ /* -- If too much time has passed, do nothing. (We could adjust our */ /* frame rate, audio quality, etc.) */ /* -------------------------------------------------------------------- */ thresh = speed->threshold; len = speed->periph.now + len - speed->tick; usec = (double)len * (4.0 / 3.579545); gettimeofday(&now, NULL); then = speed->tv; /* When will THEN be NOW??? SOON!!! */ elapsed = (now.tv_usec - then.tv_usec) + (now.tv_sec - then.tv_sec ) * 1000000; if (elapsed <= 1) elapsed = 2; /* -------------------------------------------------------------------- */ /* If more than one full second has elapsed, assume that we've gotten */ /* majorly pre-empted by the OS and just slip time so that we resync. */ /* -------------------------------------------------------------------- */ if (now.tv_sec - then.tv_sec > 1 || elapsed > 1000000) { speed_resync(speed); return len; } /* -------------------------------------------------------------------- */ /* If we're warming up, then just record the time and move on. */ /* -------------------------------------------------------------------- */ if (speed->warmup > 0) { speed->warmup--; len = elapsed * 0.89488625; speed->tv = now; speed->tick += len; return len; } /* -------------------------------------------------------------------- */ /* No time has passed. Adjust and move on. */ /* -------------------------------------------------------------------- */ if (elapsed == 0) { if (speed->periph.min_tick < speed->periph.max_tick) speed->periph.min_tick++; return 0; } /* -------------------------------------------------------------------- */ /* Too much time has elapsed. Return actual elasped time as a number */ /* of simulation ticks that has elapsed. */ /* -------------------------------------------------------------------- */ if (elapsed + thresh >= usec) { if (elapsed + thresh >= usec + LATE_TOLERANCE) { int drop = elapsed + thresh - usec - LATE_TOLERANCE; speed->gfx->drop_frame += drop >> 14; if (speed->threshold <= (speed->periph.max_tick - speed->periph.min_tick)) speed->threshold += drop >> 11; if (speed->periph.min_tick > (speed->periph.max_tick >> 3)) speed->periph.min_tick -= drop >> 11; } else { uint_32 new_thresh = speed->threshold - (speed->threshold >> 4) - 1; speed->threshold = new_thresh < speed->threshold ? new_thresh : 0; if (speed->periph.min_tick < speed->periph.max_tick) speed->periph.min_tick += 1 + ((speed->periph.max_tick-speed->periph.min_tick)>>4); } len = elapsed * 0.89488625; speed->tv = now; speed->tick += len; return len; } else if (elapsed >= usec) { uint_32 new_thresh = speed->threshold - (speed->threshold >> 3) - 1; speed->threshold = new_thresh < speed->threshold ? new_thresh : 0; } /* -------------------------------------------------------------------- */ /* Too little time has elapsed. Sit in a busy loop until enough */ /* time has gone by. If the difference is too large, then don't */ /* make it up all at once. */ /* -------------------------------------------------------------------- */ if (usec - elapsed > MICROSEC_PER_FRAME) usec = elapsed + MICROSEC_PER_FRAME; if (usec > DELAY_THRESH) plat_delay(usec / 1000); do { gettimeofday(&now, NULL); elapsed = (now.tv_usec - then.tv_usec) + (now.tv_sec - then.tv_sec ) * 1000000; } while (elapsed + thresh < usec); len = elapsed * 0.89488625; speed->tv = now; speed->tick += len; return len; } /* * ============================================================================ * SPEED_RESYNC -- Resynchronizes speed-control, slipping time. * ============================================================================ */ void speed_resync(speed_t *speed) { struct timeval tv; gettimeofday(&tv, NULL); speed->warmup = 10; speed->tv = tv; } /* * ============================================================================ * SPEED_INIT -- Initializes a speed-control object. * ============================================================================ */ int speed_init(speed_t *speed, gfx_t *gfx) { struct timeval tv; /* -------------------------------------------------------------------- */ /* Set up tick values. Who knows if these work? */ /* -------------------------------------------------------------------- */ gettimeofday(&tv, NULL); /* -------------------------------------------------------------------- */ /* Set up the speed_t structure. */ /* -------------------------------------------------------------------- */ speed->periph.read = NULL; speed->periph.write = NULL; speed->periph.peek = NULL; speed->periph.poke = NULL; speed->periph.tick = speed_tk; speed->periph.min_tick = 114; speed->periph.max_tick = 14934/2; speed->gfx = gfx; speed->threshold = 0; speed->warmup = 10; speed->tv = tv; return 0; } /* ======================================================================== */ /* 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ======================================================================== */ /* Copyright (c) 1998-1999, Joseph Zbiciak */ /* ======================================================================== */