diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 6feacfd28..dcf3808ea 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -324,43 +324,64 @@ void InitDpcThread() g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr); } -#define XBOX_TSC_FREQUENCY 733333333 // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock) -#define XBOX_ACPI_FREQUENCY 3375000 // Xbox ACPI frequency (3.375 mhz) -ULONGLONG NativeToXbox_FactorForRdtsc; -ULONGLONG NativeToXbox_FactorForAcpi; +static constexpr uint32_t XBOX_TSC_FREQUENCY = 733333333; // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock) +static constexpr uint32_t XBOX_ACPI_FREQUENCY = 3375000; // Xbox ACPI frequency (3.375 mhz) +static constexpr uint32_t SEC_TO_NSEC = 1000000000; // For seconds -> nanoseconds conversions +static uint64_t NativeToXbox_FactorForRdtsc = 0, NativeToXbox_FactorForAcpi = 0; -ULONGLONG CxbxGetPerformanceCounter(bool acpi) { - LARGE_INTEGER tsc; - ULARGE_INTEGER scaledTsc; +// State for CxbxGetPerformanceCounter - concurrent access should be next to non-existent, but secure against it anyway +static std::mutex RdtscLock, AcpiLock; +static LARGE_INTEGER LastRdtscQPC, LastAcpiQPC; +static ULONGLONG CurrentRdtsc = 0, CurrentAcpi = 0; +static ULONGLONG CurrentRdtscRemainder = 0, CurrentAcpiRemainder = 0; - QueryPerformanceCounter(&tsc); +ULONGLONG CxbxGetPerformanceCounter(bool acpi) +{ + if (acpi == false && NativeToXbox_FactorForRdtsc != 0) { + std::lock_guard lock(RdtscLock); - scaledTsc.QuadPart = 1000000000; - scaledTsc.QuadPart *= (ULONGLONG)tsc.QuadPart; + LARGE_INTEGER tsc; + QueryPerformanceCounter(&tsc); - if (acpi == false && NativeToXbox_FactorForRdtsc) { - scaledTsc.QuadPart /= NativeToXbox_FactorForRdtsc; - return scaledTsc.QuadPart; - } else if (acpi == true && NativeToXbox_FactorForRdtsc) { - scaledTsc.QuadPart /= NativeToXbox_FactorForAcpi; - return scaledTsc.QuadPart; + LARGE_INTEGER lastTsc = std::exchange(LastRdtscQPC, tsc); + tsc.QuadPart -= lastTsc.QuadPart; + tsc.QuadPart *= SEC_TO_NSEC; + tsc.QuadPart += CurrentRdtscRemainder; + ULONGLONG quotient = tsc.QuadPart / NativeToXbox_FactorForRdtsc; + ULONGLONG remainder = tsc.QuadPart % NativeToXbox_FactorForRdtsc; + + CurrentRdtscRemainder = remainder; + return CurrentRdtsc += quotient; + } else if (acpi == true && NativeToXbox_FactorForAcpi != 0) { + std::lock_guard lock(AcpiLock); + + LARGE_INTEGER tsc; + QueryPerformanceCounter(&tsc); + + LARGE_INTEGER lastTsc = std::exchange(LastAcpiQPC, tsc); + tsc.QuadPart -= lastTsc.QuadPart; + tsc.QuadPart *= SEC_TO_NSEC; + tsc.QuadPart += CurrentAcpiRemainder; + ULONGLONG quotient = tsc.QuadPart / NativeToXbox_FactorForAcpi; + ULONGLONG remainder = tsc.QuadPart % NativeToXbox_FactorForAcpi; + + CurrentAcpiRemainder = remainder; + return CurrentAcpi += quotient; } - return (uint64_t)tsc.QuadPart; + LARGE_INTEGER tsc; + QueryPerformanceCounter(&tsc); + return static_cast(tsc.QuadPart); } void CxbxInitPerformanceCounters() { - uint64_t t; - t = 1000000000; - t *= HostClockFrequency; - t /= XBOX_TSC_FREQUENCY; - NativeToXbox_FactorForRdtsc = t; + NativeToXbox_FactorForRdtsc = Muldiv64(HostClockFrequency, SEC_TO_NSEC, XBOX_TSC_FREQUENCY); + NativeToXbox_FactorForAcpi = Muldiv64(HostClockFrequency, SEC_TO_NSEC, XBOX_ACPI_FREQUENCY); - t = 1000000000; - t *= HostClockFrequency; - t /= XBOX_ACPI_FREQUENCY; - NativeToXbox_FactorForAcpi = t; + LARGE_INTEGER tsc; + QueryPerformanceCounter(&tsc); + LastRdtscQPC = LastAcpiQPC = tsc; // Let's initialize the Dpc handling thread too, // here for now (should be called by our caller)