mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-12-24 02:33:19 +00:00
spinloop timing that seeks to directly match apple speed
This commit is contained in:
parent
980bcc627d
commit
1f9a1778b9
102
src/timing.c
102
src/timing.c
@ -23,13 +23,12 @@
|
||||
|
||||
#define CALIBRATE_HZ 100
|
||||
|
||||
static unsigned long CPU_TARGET_HZ = APPLE2_HZ; // target clock speed
|
||||
static unsigned long CALIBRATE_INTERVAL = NANOSECONDS / CALIBRATE_HZ; // calibration interval for drifting
|
||||
static float CYCLE_NSECS = NANOSECONDS / (float)APPLE2_HZ; // nanosecs per cycle
|
||||
static unsigned long CPU_TARGET_HZ = APPLE2_HZ; // target clock speed
|
||||
static unsigned long CALIBRATE_INTERVAL_NSECS = NANOSECONDS / CALIBRATE_HZ; // calibration interval for drifting
|
||||
static float CYCLE_NSECS = NANOSECONDS / (float)APPLE2_HZ; // nanosecs per cycle
|
||||
|
||||
static struct timespec ti;
|
||||
|
||||
static int spinloop_count=0; // spin loop counter
|
||||
static float spin_ratio=0.0;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@ -52,10 +51,6 @@ static inline struct timespec timespec_diff(struct timespec start, struct timesp
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline long timespec_nsecs(struct timespec t) {
|
||||
return t.tv_sec*NANOSECONDS + t.tv_nsec;
|
||||
}
|
||||
|
||||
// spin loop to throttle to target CPU Hz
|
||||
static inline void _spin_loop(unsigned long c)
|
||||
{
|
||||
@ -75,7 +70,7 @@ static void _determine_initial_spinloop_counter()
|
||||
unsigned long avg_spin_nsecs = 0;
|
||||
unsigned int const samples = 5;
|
||||
unsigned int i=0;
|
||||
spinloop_count = 500000000;
|
||||
unsigned int spinloop_count = 250000000;
|
||||
do
|
||||
{
|
||||
clock_gettime(CLOCK_MONOTONIC, &s0);
|
||||
@ -85,24 +80,25 @@ static void _determine_initial_spinloop_counter()
|
||||
|
||||
if (deltat.tv_sec > 0)
|
||||
{
|
||||
printf("oops long wait (>= %lu sec) adjusting loop count (%d -> %d)\n", deltat.tv_sec, spinloop_count, spinloop_count>>1);
|
||||
LOG("oops long wait (>= %lu sec) adjusting loop count (%d -> %d)", deltat.tv_sec, spinloop_count, spinloop_count>>1);
|
||||
spinloop_count >>= 1;
|
||||
i = 0;
|
||||
avg_spin_nsecs = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("spinloop = %lu nsec\n", deltat.tv_nsec);
|
||||
LOG("spinloop = %lu nsec", deltat.tv_nsec);
|
||||
avg_spin_nsecs += deltat.tv_nsec;
|
||||
++i;
|
||||
} while (i<samples);
|
||||
|
||||
avg_spin_nsecs = (avg_spin_nsecs / samples);
|
||||
printf("average = %lu nsec\n", avg_spin_nsecs);
|
||||
LOG("average = %lu nsec , spinloop_count = %u , samples = %u", avg_spin_nsecs, spinloop_count, samples);
|
||||
|
||||
spinloop_count = CYCLE_NSECS * spinloop_count / avg_spin_nsecs;
|
||||
// counter for 1 nsec
|
||||
spin_ratio = avg_spin_nsecs / ((float)spinloop_count);
|
||||
|
||||
printf("counter for a single %fns cycle = %d\n", CYCLE_NSECS, spinloop_count);
|
||||
LOG("%fns cycle spins for average %f", CYCLE_NSECS, spin_ratio);
|
||||
}
|
||||
|
||||
void timing_initialize() {
|
||||
@ -118,32 +114,6 @@ void timing_set_cpu_scale(unsigned int scale)
|
||||
// ...
|
||||
}
|
||||
|
||||
/*
|
||||
* Calibrate emulator clock to real clock ...
|
||||
*
|
||||
* NOTE: these calculations could overflow if emulator speed is severely dampened back...
|
||||
*/
|
||||
static int _calibrate_clock (long drift_interval_nsecs)
|
||||
{
|
||||
return 0;
|
||||
// HACK FIXME : this is broken, plz debug, kthxbye!
|
||||
|
||||
struct timespec tj, deltat;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tj);
|
||||
deltat = timespec_diff(ti, tj);
|
||||
ti=tj;
|
||||
|
||||
long real_nsecs = NANOSECONDS * deltat.tv_sec + deltat.tv_nsec;
|
||||
int drift_nsecs = (int)(drift_interval_nsecs - real_nsecs); // +/- nsec drift
|
||||
int drift_count = (int)(drift_nsecs * (spinloop_count / CYCLE_NSECS) ); // +/- count drift
|
||||
|
||||
// adjust spinloop_count ...
|
||||
spinloop_count += drift_count / CALIBRATE_INTERVAL;
|
||||
|
||||
// ideally we should return a sub-interval here ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Throttles 6502 CPU down to the target CPU frequency (default is speed of original Apple //e).
|
||||
*
|
||||
@ -151,32 +121,44 @@ static int _calibrate_clock (long drift_interval_nsecs)
|
||||
*/
|
||||
void timing_throttle()
|
||||
{
|
||||
static float drift_interval=0.0;
|
||||
static int spinloop_adjust=0;
|
||||
struct timespec tj, deltat;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tj);
|
||||
deltat = timespec_diff(ti, tj);
|
||||
ti=tj;
|
||||
|
||||
static time_t severe_lag=0;
|
||||
if (deltat.tv_sec != 0)
|
||||
{
|
||||
// severely lagging...
|
||||
if (severe_lag < time(NULL))
|
||||
{
|
||||
severe_lag = time(NULL)+2;
|
||||
LOG("Severe lag detected...");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t opcycles = cpu65__opcycles[cpu65_debug.opcode] + cpu65_debug.opcycles;
|
||||
uint8_t c=0;
|
||||
if (spinloop_adjust < 0)
|
||||
unsigned long opcycles_nsecs = opcycles * (CYCLE_NSECS/2);
|
||||
|
||||
if (deltat.tv_nsec >= opcycles_nsecs)
|
||||
{
|
||||
c=-1;
|
||||
++spinloop_adjust;
|
||||
}
|
||||
else if (spinloop_adjust > 0)
|
||||
{
|
||||
c=1;
|
||||
--spinloop_adjust;
|
||||
// lagging
|
||||
return;
|
||||
}
|
||||
|
||||
// spin for the desired/estimated number of nsecs
|
||||
_spin_loop(opcycles * (spinloop_count+c));
|
||||
unsigned long diff_nsec = opcycles_nsecs - deltat.tv_nsec;
|
||||
|
||||
drift_interval += opcycles*CYCLE_NSECS;
|
||||
|
||||
if (drift_interval > CALIBRATE_INTERVAL)
|
||||
static time_t sample_time=0;
|
||||
if (sample_time < time(NULL))
|
||||
{
|
||||
// perform calibration
|
||||
spinloop_adjust = _calibrate_clock((long)drift_interval);
|
||||
drift_interval = 0.0;
|
||||
sample_time = time(NULL)+1;
|
||||
//LOG("sample diff_nsec : %lu", diff_nsec);
|
||||
}
|
||||
|
||||
unsigned long spin_count = spin_ratio * diff_nsec;
|
||||
|
||||
// spin for the rest of the interval ...
|
||||
_spin_loop(spin_count);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user