Source to src/host.c
#include "config.h"
#if HAVE_NANOSLEEP
#ifdef __MINGW32__
#include <unistd.h>
#else
#include <sys/time.h>
#endif
#endif
#include <errno.h>
#include "host.h"
#include "configuration.h"
#include "main.h"
/* NeXTdimension blank handling, see nd_sdl.c */
void nd_display_blank(int num);
void nd_video_blank(int num);
#define NUM_BLANKS 3
static const char* BLANKS[] = {
"main","nd_main","nd_video"
};
static volatile Uint32 blank[NUM_BLANKS];
static Uint32 vblCounter[NUM_BLANKS];
static Uint64 perfCounterStart;
static Sint64 cycleCounterStart;
static double cycleSecsStart;
static bool isRealtime;
static bool oldIsRealtime;
static double cycleDivisor;
static lock_t timeLock;
static Uint32 ticksStart;
static bool enableRealtime;
static Uint64 hardClockExpected;
static Uint64 hardClockActual;
static time_t unixTimeStart;
static double unixTimeOffset = 0;
static double perfFrequency;
static double realTimeOffset;
static Uint64 pauseTimeStamp;
static bool osDarkmatter;
void host_reset() {
perfCounterStart = SDL_GetPerformanceCounter();
pauseTimeStamp = perfCounterStart;
perfFrequency = SDL_GetPerformanceFrequency();
ticksStart = SDL_GetTicks();
unixTimeStart = time(NULL);
cycleCounterStart = 0;
cycleSecsStart = 0;
isRealtime = false;
oldIsRealtime = false;
hardClockExpected = 0;
hardClockActual = 0;
enableRealtime = ConfigureParams.System.bRealtime;
realTimeOffset = 0;
osDarkmatter = false;
for(int i = NUM_BLANKS; --i >= 0;) {
vblCounter[i] = 0;
blank[i] = 0;
}
cycleDivisor = ConfigureParams.System.nCpuFreq * 1000 * 1000;
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
}
void host_blank(int slot, int src, bool state) {
int bit = 1 << slot;
if(state) {
blank[src] |= bit;
vblCounter[src]++;
}
else
blank[src] &= ~bit;
switch (src) {
case ND_DISPLAY: nd_display_blank(slot); break;
case ND_VIDEO: nd_video_blank(slot); break;
}
}
bool host_blank_state(int slot, int src) {
int bit = 1 << slot;
return blank[src] & bit;
}
void host_hardclock(int expected, int actual) {
if(abs(actual-expected) > 1000)
fprintf(stderr, "[Hardclock] expected:%dus actual:%dus\n", expected, actual);
else {
hardClockExpected += expected;
hardClockActual += actual;
}
}
extern Sint64 nCyclesMainCounter;
void host_realtime(bool state) {
isRealtime = state;
}
double host_time_sec() {
double rt;
double vt;
host_time(&rt, &vt);
return vt;
}
void host_time(double* realTime, double* hostTime) {
host_lock(&timeLock);
*realTime = (SDL_GetPerformanceCounter() - perfCounterStart);
*realTime /= perfFrequency;
if(oldIsRealtime) {
*hostTime = *realTime;
} else {
*hostTime = nCyclesMainCounter - cycleCounterStart;
*hostTime /= cycleDivisor;
*hostTime += cycleSecsStart;
}
bool state = (isRealtime || osDarkmatter) && enableRealtime;
if(oldIsRealtime != state) {
if(oldIsRealtime) {
// switching from real-time to cycle-time
cycleSecsStart = *realTime;
cycleCounterStart = nCyclesMainCounter;
} else {
// switching from cycle-time to real-time
realTimeOffset = *hostTime - *realTime;
if(realTimeOffset > 0) {
// if hostTime is in the future, wait until realTime is there as well
if(realTimeOffset > 0.01)
host_sleep_sec(realTimeOffset);
else
while(*realTime < *hostTime) {
*realTime = (SDL_GetPerformanceCounter() - perfCounterStart);
*realTime /= perfFrequency;
}
}
}
oldIsRealtime = state;
}
realTimeOffset = *hostTime - *realTime;
host_unlock(&timeLock);
}
// Return current time as micro seconds
Uint64 host_time_us() {
return host_time_sec() * 1000.0 * 1000.0;
}
// Return current time as milliseconds
Uint32 host_time_ms() {
return host_time_us() / 1000LL;
}
time_t host_unix_time() {
return unixTimeStart + unixTimeOffset + host_time_sec();
}
void host_set_unix_time(time_t now) {
unixTimeOffset += difftime(now, host_unix_time());
}
double host_real_time_offset() {
host_lock(&timeLock);
double result = realTimeOffset;
host_unlock(&timeLock);
return result;
}
void host_pause_time(bool pausing) {
if(pausing) {
pauseTimeStamp = SDL_GetPerformanceCounter();
} else {
perfCounterStart += SDL_GetPerformanceCounter() - pauseTimeStamp;
}
}
/*-----------------------------------------------------------------------*/
/**
* Sleep for a given number of micro seconds. We burn cycles by running
* the event loop.
*/
void host_sleep_us(Uint64 us) {
#if HAVE_NANOSLEEP
struct timespec ts;
int ret;
ts.tv_sec = us / 1000000;
ts.tv_nsec = (us % 1000000) * 1000; /* micro sec -> nano sec */
/* wait until all the delay is elapsed, including possible interruptions by signals */
do {
errno = 0;
ret = nanosleep(&ts, &ts);
} while ( ret && ( errno == EINTR ) ); /* keep on sleeping if we were interrupted */
#else
Uint64 timeout = host_time_us() + us;
host_sleep_ms(( (Uint32)(ticks_micro / 1000) ) ; /* micro sec -> milli sec */
while(host_time_us() < timeout) {}
#endif
}
void host_sleep_ms(Uint32 ms) {
Uint64 sleep = ms;
sleep *= 1000;
host_sleep_us(sleep);
}
void host_sleep_sec(double sec) {
sec *= 1000 * 1000;
host_sleep_us((Uint64)sec);
}
void host_lock(lock_t* lock) {
SDL_AtomicLock(lock);
}
int host_trylock(lock_t* lock) {
return SDL_AtomicTryLock(lock);
}
void host_unlock(lock_t* lock) {
SDL_AtomicUnlock(lock);
}
thread_t* host_thread_create(thread_func_t func, void* data) {
return SDL_CreateThread(func, "Thread", data);
}
int host_thread_wait(thread_t* thread) {
int status;
SDL_WaitThread(thread, &status);
return status;
}
int host_num_cpus() {
return SDL_GetCPUCount();
}
void host_darkmatter(bool state) {
osDarkmatter = state;
}
static double lastVT;
static char report[512];
const char* host_report(double realTime, double hostTime) {
double dVT = hostTime - lastVT;
double hardClock = hardClockExpected;
hardClock /= hardClockActual == 0 ? 1 : hardClockActual;
char* r = report;
r += sprintf(r, "[%s] hostTime:%.1f hardClock:%.3fMHz", enableRealtime ? "Max.speed" : "CycleTime", hostTime, hardClock);
for(int i = NUM_BLANKS; --i >= 0;) {
r += sprintf(r, " %s:%.1fHz", BLANKS[i], (double)vblCounter[i]/dVT);
vblCounter[i] = 0;
}
lastVT = hostTime;
return report;
}