Implemented PTIMER alarm interrupt of NV2A + fixed a bug in timer_init
This fixes DOAU showing the dirty disk error in PAL50 mode
This commit is contained in:
parent
ccd77fcf4d
commit
709108b045
|
@ -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<LARGE_INTEGER *>(&HostQPCFrequency));
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&last_qpc));
|
||||
pit_last_qpc = last_qpc;
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&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<uint64_t, 4> next;
|
||||
std::array<uint64_t, 5> 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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<uint64_t, std::nano>(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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue