save fpu state properly-ish, add a guard to doom_malloc_wrapper, fix QueryPerformance* and GetSystemTimeAsFileTime
This commit is contained in:
1 parent
def649b995
commit
ddb5d5f5d8
8 files changed
+78
-56
No files matched your search
+33
-48
@@ -1139,69 +1139,51 @@ __declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (v
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timing & Performance Counters
|
||||
//
|
||||
// We use the CPU's Time Stamp Counter (TSC) for QueryPerformanceCounter so
|
||||
// that callers like Bink get a truly high-resolution, continuously-advancing
|
||||
// clock. The TSC frequency is measured once on first use.
|
||||
//
|
||||
// Calibration strategy:
|
||||
// 1. Try CPUID leaf 0x15 (TSC / Crystal-clock ratio). If the crystal
|
||||
// frequency (ECX) is non-zero, compute TSC Hz = crystal_hz * EBX / EAX
|
||||
// with no blocking at all.
|
||||
// 2. Otherwise fall back to measuring TSC ticks over a known wall-clock
|
||||
// interval supplied by SysGetTick(). SysGetTick() returns raw LAPIC
|
||||
// interrupt counts; each unit represents QUANTUM_MS = 10 ms. We wait
|
||||
// for two consecutive tick-boundary crossings and divide.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define QUANTUM_MS 10 // must match hal_timer.c QUANTUM_MS
|
||||
|
||||
typedef union {
|
||||
struct { DWORD LowPart; long HighPart; } u;
|
||||
long long QuadPart;
|
||||
} LARGE_INTEGER;
|
||||
|
||||
static volatile long long gTscFreq = 0; // TSC ticks per second; 0 = uninitialised
|
||||
static volatile long long gTscFreq = 0;
|
||||
|
||||
// Attempt to read TSC frequency from CPUID leaf 0x15.
|
||||
// Returns 0 if the leaf is absent or the crystal frequency is not enumerated.
|
||||
// Attempt to read TSC frequency from CPUID leaf 0x15 (Modern Intel/AMD).
|
||||
static long long TscFreqFromCpuid(void) {
|
||||
int info[4];
|
||||
__cpuid(info, 0);
|
||||
if ((unsigned)info[0] < 0x15) return 0;
|
||||
|
||||
__cpuid(info, 0x15);
|
||||
unsigned denom = (unsigned)info[0]; // EAX – TSC/crystal denominator
|
||||
unsigned numer = (unsigned)info[1]; // EBX – TSC/crystal numerator
|
||||
unsigned crystal_hz = (unsigned)info[2]; // ECX – crystal clock in Hz
|
||||
unsigned denom = (unsigned)info[0];
|
||||
unsigned numer = (unsigned)info[1];
|
||||
unsigned crystal_hz = (unsigned)info[2];
|
||||
|
||||
if (!denom || !numer || !crystal_hz) return 0;
|
||||
return (long long)crystal_hz * numer / denom;
|
||||
}
|
||||
|
||||
// Calibrate TSC frequency by timing over two LAPIC-tick boundaries.
|
||||
// Each SysGetTick() unit = QUANTUM_MS ms; we measure across N_TICKS units
|
||||
// for a total gate time of N_TICKS * QUANTUM_MS ms.
|
||||
// Calibrate TSC against SysGetTick (1 unit = 1 microsecond).
|
||||
static long long TscFreqCalibrate(void) {
|
||||
const uint64_t N_TICKS = 10; // 10 * 10 ms = 100 ms gate time
|
||||
// We'll wait for 100ms (100,000 microseconds) to get a stable sample.
|
||||
const uint64_t CALIBRATION_US = 100000;
|
||||
|
||||
// Synchronise to the leading edge of a tick.
|
||||
uint64_t t = SysGetTick();
|
||||
while (SysGetTick() == t) { _mm_pause(); }
|
||||
|
||||
uint64_t tsc0 = __rdtsc();
|
||||
uint64_t t0 = SysGetTick();
|
||||
uint64_t tsc0 = __rdtsc();
|
||||
|
||||
// Wait for N_TICKS more ticks.
|
||||
while ((SysGetTick() - t0) < N_TICKS) { _mm_pause(); }
|
||||
// Simple busy wait for the duration
|
||||
while ((SysGetTick() - t0) < CALIBRATION_US) {
|
||||
_mm_pause();
|
||||
}
|
||||
|
||||
uint64_t tsc1 = __rdtsc();
|
||||
uint64_t ticks_got = SysGetTick() - t0; // actual ticks elapsed (>= N_TICKS)
|
||||
uint64_t t1 = SysGetTick();
|
||||
uint64_t tsc1 = __rdtsc();
|
||||
|
||||
// ticks_got LAPIC ticks = ticks_got * QUANTUM_MS ms = ticks_got * QUANTUM_MS / 1000 s
|
||||
// TSC Hz = (tsc1-tsc0) / (ticks_got * QUANTUM_MS / 1000)
|
||||
// = (tsc1-tsc0) * 1000 / (ticks_got * QUANTUM_MS)
|
||||
return (long long)((tsc1 - tsc0) * 1000ULL / (ticks_got * (uint64_t)QUANTUM_MS));
|
||||
uint64_t delta_tsc = tsc1 - tsc0;
|
||||
uint64_t delta_us = t1 - t0;
|
||||
|
||||
// Hz = (Delta TSC / Delta Microseconds) * 1,000,000
|
||||
return (long long)(delta_tsc * 1000000ULL / delta_us);
|
||||
}
|
||||
|
||||
static long long TscFreqGet(void) {
|
||||
@@ -1220,8 +1202,7 @@ __declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency)
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) {
|
||||
// Ensure frequency is measured before the first counter read so that the
|
||||
// baseline TSC and the frequency come from the same calibration window.
|
||||
// Ensure calibration happens before the first read.
|
||||
TscFreqGet();
|
||||
if (lpPerformanceCount)
|
||||
lpPerformanceCount->QuadPart = (long long)__rdtsc();
|
||||
@@ -1229,15 +1210,19 @@ __declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceC
|
||||
}
|
||||
|
||||
__declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) {
|
||||
// Return a fixed FILETIME representing 2024-01-01 00:00:00 UTC.
|
||||
// FILETIME = 100-nanosecond intervals since 1601-01-01.
|
||||
// 2024-01-01 offset: 133186080000000000 * 100 ns
|
||||
if (!lpSystemTimeAsFileTime) return;
|
||||
// SysGetTick() units = QUANTUM_MS ms = QUANTUM_MS * 10000 * 100-ns intervals.
|
||||
unsigned long long ft = 133186080000000000ULL
|
||||
+ SysGetTick() * ((unsigned long long)QUANTUM_MS * 10000ULL);
|
||||
unsigned char* p = (unsigned char*)lpSystemTimeAsFileTime;
|
||||
for (int i = 0; i < 8; i++) p[i] = (unsigned char)(ft >> (i * 8));
|
||||
|
||||
// Baseline: 2024-01-01 00:00:00 UTC in 100-ns intervals
|
||||
const unsigned long long BASELINE_FT = 133485408000000000ULL;
|
||||
|
||||
// SysGetTick() is in microseconds.
|
||||
// 1 microsecond = 10 units of 100-nanoseconds.
|
||||
unsigned long long now_us = SysGetTick();
|
||||
unsigned long long ft = BASELINE_FT + (now_us * 10ULL);
|
||||
|
||||
// Copy the 64-bit value into the output buffer.
|
||||
// We use memcpy to safely handle potential alignment issues with the void pointer.
|
||||
memcpy(lpSystemTimeAsFileTime, &ft, sizeof(ft));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user