From 709108b045543e989e5dbd28c78dc38e5c367401 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Tue, 28 Mar 2023 00:02:34 +0200 Subject: [PATCH] Implemented PTIMER alarm interrupt of NV2A + fixed a bug in timer_init This fixes DOAU showing the dirty disk error in PAL50 mode --- src/common/Timer.cpp | 17 +++++++------ src/devices/video/EmuNV2A_PRAMDAC.cpp | 1 + src/devices/video/EmuNV2A_PTIMER.cpp | 18 ++++++++------ src/devices/video/nv2a.cpp | 35 +++++++++++++++++++++++++++ src/devices/video/nv2a.h | 1 + src/devices/video/nv2a_int.h | 3 +++ 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/common/Timer.cpp b/src/common/Timer.cpp index a4fe0f76f..c361e2750 100644 --- a/src/common/Timer.cpp +++ b/src/common/Timer.cpp @@ -42,8 +42,8 @@ #include "core\hle\DSOUND\DirectSound\DirectSoundGlobal.hpp" -static uint64_t last_qpc; // last time when QPC was called -static uint64_t exec_time; // total execution time in us since the emulation started +static std::atomic_uint64_t last_qpc; // last time when QPC was called +static std::atomic_uint64_t exec_time; // total execution time in us since the emulation started static uint64_t pit_last; // last time when the pit time was updated static uint64_t pit_last_qpc; // last QPC time of the pit // The frequency of the high resolution clock of the host, and the start time @@ -53,8 +53,8 @@ int64_t HostQPCFrequency, HostQPCStartTime; void timer_init() { QueryPerformanceFrequency(reinterpret_cast(&HostQPCFrequency)); - QueryPerformanceCounter(reinterpret_cast(&last_qpc)); - pit_last_qpc = last_qpc; + QueryPerformanceCounter(reinterpret_cast(&HostQPCStartTime)); + pit_last_qpc = last_qpc = HostQPCStartTime; pit_last = get_now(); // Synchronize xbox system time with host time @@ -140,11 +140,12 @@ uint64_t get_now() static uint64_t get_next(uint64_t now) { - std::array next; + std::array next; next[0] = g_NV2A->vblank_next(now); - next[1] = pit_next(now); - next[2] = g_USB0->m_HostController->OHCI_next(now); - next[3] = dsound_next(now); + next[1] = g_NV2A->ptimer_next(now); + next[2] = pit_next(now); + next[3] = g_USB0->m_HostController->OHCI_next(now); + next[4] = dsound_next(now); return *std::min_element(next.begin(), next.end()); } diff --git a/src/devices/video/EmuNV2A_PRAMDAC.cpp b/src/devices/video/EmuNV2A_PRAMDAC.cpp index 768811e3a..c0e9db80f 100644 --- a/src/devices/video/EmuNV2A_PRAMDAC.cpp +++ b/src/devices/video/EmuNV2A_PRAMDAC.cpp @@ -82,6 +82,7 @@ DEVICE_WRITE32(PRAMDAC) } else { d->pramdac.core_clock_freq = (NV2A_CRYSTAL_FREQ * n) / (1 << p) / m; + d->ptimer_period = ((uint64_t(d->ptimer.alarm_time >> 5) * SCALE_S_IN_US) / d->pramdac.core_clock_freq); } break; diff --git a/src/devices/video/EmuNV2A_PTIMER.cpp b/src/devices/video/EmuNV2A_PTIMER.cpp index d6f6f42f5..5711537b8 100644 --- a/src/devices/video/EmuNV2A_PTIMER.cpp +++ b/src/devices/video/EmuNV2A_PTIMER.cpp @@ -35,17 +35,13 @@ #include "common\util\CxbxUtil.h" -#define NANOSECONDS_PER_SECOND 1000000000 /* PTIMER - time measurement and time-based alarms */ -static uint64_t ptimer_get_clock(NV2AState * d) +static uint64_t ptimer_get_clock(NV2AState *d) { - // Get time in nanoseconds - uint64_t time = std::chrono::duration(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return Muldiv64(Muldiv64(time, + return Muldiv64(Muldiv64(get_now(), (uint32_t)d->pramdac.core_clock_freq, // TODO : Research how this can be updated to accept uint64_t - NANOSECONDS_PER_SECOND), // Was CLOCKS_PER_SEC + SCALE_S_IN_US), // Was CLOCKS_PER_SEC d->ptimer.denominator, d->ptimer.numerator); } @@ -91,6 +87,13 @@ DEVICE_WRITE32(PTIMER) break; case NV_PTIMER_INTR_EN_0: d->ptimer.enabled_interrupts = value; + if (d->ptimer.enabled_interrupts & NV_PTIMER_INTR_EN_0_ALARM) { + d->ptimer_last = get_now(); + d->ptimer_active = true; + } + else if ((d->ptimer.enabled_interrupts & NV_PTIMER_INTR_EN_0_ALARM) == 0) { + d->ptimer_active = false; + } update_irq(d); break; case NV_PTIMER_DENOMINATOR: @@ -101,6 +104,7 @@ DEVICE_WRITE32(PTIMER) break; case NV_PTIMER_ALARM_0: d->ptimer.alarm_time = value; + d->ptimer_period = ((uint64_t(d->ptimer.alarm_time >> 5) * SCALE_S_IN_US) / d->pramdac.core_clock_freq); break; default: //DEVICE_WRITE32_REG(ptimer); // Was : DEBUG_WRITE32_UNHANDLED(PTIMER); diff --git a/src/devices/video/nv2a.cpp b/src/devices/video/nv2a.cpp index 92522ad16..a2a13397e 100644 --- a/src/devices/video/nv2a.cpp +++ b/src/devices/video/nv2a.cpp @@ -130,6 +130,14 @@ static void update_irq(NV2AState *d) d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PVIDEO; } + /* PTIMER */ + if (d->ptimer.pending_interrupts & d->ptimer.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PTIMER; + } + else { + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PTIMER; + } + /* TODO : PBUS * / if (d->pbus.pending_interrupts & d->pbus.enabled_interrupts) { d->pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS; @@ -1401,3 +1409,30 @@ uint64_t NV2ADevice::vblank_next(uint64_t now) return m_nv2a_state->vblank_last + vblank_period - now; // time remaining until next vblank } + +uint64_t NV2ADevice::ptimer_next(uint64_t now) +{ + // Test case: Dead or Alive Ultimate uses this when in PAL50 mode only + if (m_nv2a_state->ptimer_active) { + const uint64_t ptimer_period = m_nv2a_state->ptimer_period; + uint64_t next = m_nv2a_state->ptimer_last + ptimer_period; + + if (now >= next) { + if (!m_nv2a_state->exiting) [[likely]] { + m_nv2a_state->ptimer.pending_interrupts |= NV_PTIMER_INTR_0_ALARM; + update_irq(m_nv2a_state); + + // trigger the gpu interrupt if it was asserted in update_irq + if (g_bEnableAllInterrupts && HalSystemInterrupts[3].IsPending() && EmuInterruptList[3] && EmuInterruptList[3]->Connected) { + HalSystemInterrupts[3].Trigger(EmuInterruptList[3]); + } + } + m_nv2a_state->ptimer_last = get_now(); + return ptimer_period; + } + + return m_nv2a_state->ptimer_last + ptimer_period - now; // time remaining until next ptimer interrupt + } + + return -1; +} diff --git a/src/devices/video/nv2a.h b/src/devices/video/nv2a.h index 881a16acd..03fc3c81a 100644 --- a/src/devices/video/nv2a.h +++ b/src/devices/video/nv2a.h @@ -110,6 +110,7 @@ public: static int GetFrameHeight(NV2AState *d); uint64_t vblank_next(uint64_t now); + uint64_t ptimer_next(uint64_t now); private: NV2AState *m_nv2a_state; diff --git a/src/devices/video/nv2a_int.h b/src/devices/video/nv2a_int.h index ffeb539bb..ceef33c73 100644 --- a/src/devices/video/nv2a_int.h +++ b/src/devices/video/nv2a_int.h @@ -363,6 +363,9 @@ typedef struct NV2AState { // qemu_irq irq; bool exiting; bool enable_overlay = false; + bool ptimer_active = false; + uint64_t ptimer_last; + uint64_t ptimer_period; // VGACommonState vga; // GraphicHwOps hw_ops;