mirror of https://github.com/PCSX2/pcsx2.git
Core: Refactor a lot of timer work and fix a couple of bugs
EE/IOP Timers: improve clock sync, disable v/h sync when SINT enabled. Some changes based on tests from PS2 [SAVEVERSION+]
This commit is contained in:
parent
67f1d6e24b
commit
566ea8ea9b
|
@ -7,6 +7,7 @@
|
|||
#include "CDVD/IsoReader.h"
|
||||
#include "CDVD/IsoFileFormats.h"
|
||||
#include "GS.h"
|
||||
#include "SIO/Sio.h"
|
||||
#include "Elfheader.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
|
@ -1553,13 +1554,20 @@ void cdvdUpdateTrayState()
|
|||
|
||||
void cdvdVsync()
|
||||
{
|
||||
// We're counting in frames, but one second isn't exactly 50 or 60 frames in most cases, so we'll keep the fractions.
|
||||
cdvd.RTCcount++;
|
||||
if (cdvd.RTCcount < GetVerticalFrequency())
|
||||
const double verticalFrequency = GetVerticalFrequency();
|
||||
if (cdvd.RTCcount < verticalFrequency)
|
||||
return;
|
||||
cdvd.RTCcount = 0;
|
||||
|
||||
cdvd.RTCcount -= verticalFrequency;
|
||||
|
||||
cdvdUpdateTrayState();
|
||||
|
||||
// FolderMemoryCard needs information on how much time has passed since the last write
|
||||
// Call it every second.
|
||||
sioNextFrame();
|
||||
|
||||
cdvd.RTC.second++;
|
||||
if (cdvd.RTC.second < 60)
|
||||
return;
|
||||
|
|
|
@ -123,7 +123,7 @@ struct cdvdStruct
|
|||
|
||||
// Calculates the number of Vsyncs and once it reaches a total number of Vsyncs worth a second with respect to
|
||||
// the videomode's vertical frequency, it updates the real time clock.
|
||||
int RTCcount;
|
||||
double RTCcount;
|
||||
cdvdRTC RTC;
|
||||
|
||||
u32 CurrentSector;
|
||||
|
|
|
@ -31,8 +31,8 @@ Counter counters[4];
|
|||
SyncCounter hsyncCounter;
|
||||
SyncCounter vsyncCounter;
|
||||
|
||||
u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate()
|
||||
s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate()
|
||||
u32 nextStartCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate()
|
||||
s32 nextDeltaCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate()
|
||||
|
||||
// Forward declarations needed because C/C++ both are wimpy single-pass compilers.
|
||||
|
||||
|
@ -59,7 +59,7 @@ static bool IsProgressiveVideoMode()
|
|||
void rcntReset(int index)
|
||||
{
|
||||
counters[index].count = 0;
|
||||
counters[index].sCycleT = cpuRegs.cycle;
|
||||
counters[index].startCycle = cpuRegs.cycle;
|
||||
}
|
||||
|
||||
// Updates the state of the nextCounter value (if needed) to serve
|
||||
|
@ -76,13 +76,13 @@ static __fi void _rcntSet(int cntidx)
|
|||
if (!rcntCanCount(cntidx) || (counter.mode.ClockSource == 0x3))
|
||||
return;
|
||||
|
||||
if (!counter.mode.TargetInterrupt && !counter.mode.OverflowInterrupt)
|
||||
if (!counter.mode.TargetInterrupt && !counter.mode.OverflowInterrupt && !counter.mode.ZeroReturn)
|
||||
return;
|
||||
// check for special cases where the overflow or target has just passed
|
||||
// (we probably missed it because we're doing/checking other things)
|
||||
if (counter.count > 0x10000 || counter.count > counter.target)
|
||||
{
|
||||
nextCounter = 4;
|
||||
nextDeltaCounter = 4;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -91,13 +91,14 @@ static __fi void _rcntSet(int cntidx)
|
|||
// that into account. Adding the difference from that cycle count to the current one
|
||||
// will do the trick!
|
||||
|
||||
c = ((0x10000 - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
|
||||
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
|
||||
if (c < nextCounter)
|
||||
{
|
||||
nextCounter = c;
|
||||
c = ((0x10000 - counter.count) * counter.rate) - (cpuRegs.cycle - counter.startCycle);
|
||||
c += cpuRegs.cycle - nextStartCounter; // adjust for time passed since last rcntUpdate();
|
||||
|
||||
cpuSetNextEvent(nextsCounter, nextCounter); // Need to update on counter resets/target changes
|
||||
if (c < nextDeltaCounter)
|
||||
{
|
||||
nextDeltaCounter = c;
|
||||
|
||||
cpuSetNextEvent(nextStartCounter, nextDeltaCounter); // Need to update on counter resets/target changes
|
||||
}
|
||||
|
||||
// Ignore target diff if target is currently disabled.
|
||||
|
@ -111,12 +112,13 @@ static __fi void _rcntSet(int cntidx)
|
|||
else
|
||||
{
|
||||
|
||||
c = ((counter.target - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
|
||||
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
|
||||
if (c < nextCounter)
|
||||
c = ((counter.target - counter.count) * counter.rate) - (cpuRegs.cycle - counter.startCycle);
|
||||
c += cpuRegs.cycle - nextStartCounter; // adjust for time passed since last rcntUpdate();
|
||||
|
||||
if (c < nextDeltaCounter)
|
||||
{
|
||||
nextCounter = c;
|
||||
cpuSetNextEvent(nextsCounter, nextCounter); // Need to update on counter resets/target changes
|
||||
nextDeltaCounter = c;
|
||||
cpuSetNextEvent(nextStartCounter, nextDeltaCounter); // Need to update on counter resets/target changes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,22 +129,22 @@ static __fi void cpuRcntSet()
|
|||
int i;
|
||||
|
||||
// Default to next VBlank
|
||||
nextsCounter = cpuRegs.cycle;
|
||||
nextCounter = vsyncCounter.CycleT - (cpuRegs.cycle - vsyncCounter.sCycle);
|
||||
nextStartCounter = cpuRegs.cycle;
|
||||
nextDeltaCounter = vsyncCounter.deltaCycles - (cpuRegs.cycle - vsyncCounter.startCycle);
|
||||
|
||||
// Also check next HSync
|
||||
s32 nextHsync = hsyncCounter.CycleT - (cpuRegs.cycle - hsyncCounter.sCycle);
|
||||
if (nextHsync < nextCounter)
|
||||
nextCounter = nextHsync;
|
||||
s32 nextHsync = hsyncCounter.deltaCycles - (cpuRegs.cycle - hsyncCounter.startCycle);
|
||||
if (nextHsync < nextDeltaCounter)
|
||||
nextDeltaCounter = nextHsync;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
_rcntSet(i);
|
||||
|
||||
// sanity check!
|
||||
if (nextCounter < 0)
|
||||
nextCounter = 0;
|
||||
if (nextDeltaCounter < 0)
|
||||
nextDeltaCounter = 0;
|
||||
|
||||
cpuSetNextEvent(nextsCounter, nextCounter); // Need to update on counter resets/target changes
|
||||
cpuSetNextEvent(nextStartCounter, nextDeltaCounter); // Need to update on counter resets/target changes
|
||||
}
|
||||
|
||||
|
||||
|
@ -187,11 +189,11 @@ void rcntInit()
|
|||
gsIsInterlaced = VMManager::Internal::IsFastBootInProgress();
|
||||
|
||||
hsyncCounter.Mode = MODE_HRENDER;
|
||||
hsyncCounter.sCycle = cpuRegs.cycle;
|
||||
vsyncCounter.CycleT = vSyncInfo.hRender;
|
||||
hsyncCounter.startCycle = cpuRegs.cycle;
|
||||
hsyncCounter.deltaCycles = vSyncInfo.hRender;
|
||||
vsyncCounter.Mode = MODE_VRENDER;
|
||||
vsyncCounter.CycleT = vSyncInfo.Render;
|
||||
vsyncCounter.sCycle = cpuRegs.cycle;
|
||||
vsyncCounter.deltaCycles = vSyncInfo.Render;
|
||||
vsyncCounter.startCycle = cpuRegs.cycle;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
rcntReset(i);
|
||||
|
@ -240,31 +242,18 @@ static void vSyncInfoCalc(vSyncTimingInfo* info, double framesPerSecond, u32 sca
|
|||
info->GSBlank = (u32)(GSBlank / 10000);
|
||||
info->Render = (u32)(Render / 10000);
|
||||
info->Blank = (u32)(Blank / 10000);
|
||||
const u64 accumilated_vrender = (Render % 10000) + (Blank % 10000);
|
||||
info->Render += (u32)(accumilated_vrender / 10000);
|
||||
|
||||
info->hRender = (u32)(hRender / 10000);
|
||||
info->hBlank = (u32)(hBlank / 10000);
|
||||
info->hScanlinesPerFrame = scansPerFrame;
|
||||
|
||||
if ((Render % 10000) >= 5000)
|
||||
info->Render++;
|
||||
if ((Blank % 10000) >= 5000)
|
||||
info->Blank++;
|
||||
const u64 accumilatedHRenderError = (hRender % 10000) + (hBlank % 10000);
|
||||
const u64 accumilatedHFractional = accumilatedHRenderError % 10000;
|
||||
info->hRender += (u32)(accumilatedHRenderError / 10000);
|
||||
info->hSyncError = (accumilatedHFractional * (scansPerFrame / (IsInterlacedVideoMode() ? 2 : 1))) / 10000;
|
||||
|
||||
if ((hRender % 10000) >= 5000)
|
||||
info->hRender++;
|
||||
if ((hBlank % 10000) >= 5000)
|
||||
info->hBlank++;
|
||||
|
||||
// Calculate accumulative hSync rounding error per half-frame:
|
||||
if (IsInterlacedVideoMode()) // gets off the chart in that mode
|
||||
{
|
||||
u32 hSyncCycles = ((info->hRender + info->hBlank) * scansPerFrame) / 2;
|
||||
u32 vSyncCycles = (info->Render + info->Blank);
|
||||
info->hSyncError = vSyncCycles - hSyncCycles;
|
||||
//Console.Warning("%d",info->hSyncError);
|
||||
}
|
||||
else
|
||||
info->hSyncError = 0;
|
||||
// Note: In NTSC modes there is some small rounding error in the vsync too,
|
||||
// however it would take thousands of frames for it to amount to anything and
|
||||
// is thus not worth the effort at this time.
|
||||
|
@ -419,10 +408,16 @@ void UpdateVSyncRate(bool force)
|
|||
if (custom && video_mode_initialized)
|
||||
Console.WriteLn(Color_StrongGreen, " ... with user configured refresh rate: %.02f Hz", vertical_frequency);
|
||||
|
||||
hsyncCounter.CycleT = (hsyncCounter.Mode == MODE_HBLANK) ? vSyncInfo.hBlank : vSyncInfo.hRender;
|
||||
vsyncCounter.CycleT = (vsyncCounter.Mode == MODE_GSBLANK) ?
|
||||
s32 hdiff = hsyncCounter.deltaCycles;
|
||||
s32 vdiff = vsyncCounter.deltaCycles;
|
||||
hsyncCounter.deltaCycles = (hsyncCounter.Mode == MODE_HBLANK) ? vSyncInfo.hBlank : vSyncInfo.hRender;
|
||||
vsyncCounter.deltaCycles = (vsyncCounter.Mode == MODE_GSBLANK) ?
|
||||
vSyncInfo.GSBlank :
|
||||
((vsyncCounter.Mode == MODE_VSYNC) ? vSyncInfo.Blank : vSyncInfo.Render);
|
||||
((vsyncCounter.Mode == MODE_VBLANK) ? vSyncInfo.Blank : vSyncInfo.Render);
|
||||
|
||||
hsyncCounter.startCycle += hdiff - hsyncCounter.deltaCycles;
|
||||
vsyncCounter.startCycle += vdiff - vsyncCounter.deltaCycles;
|
||||
|
||||
cpuRcntSet();
|
||||
|
||||
VMManager::Internal::FrameRateChanged();
|
||||
|
@ -504,15 +499,17 @@ static __fi void VSyncStart(u32 sCycle)
|
|||
if (EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
|
||||
SysTrace.EE.Counters.Write(" ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount);
|
||||
|
||||
hwIntcIrq(INTC_VBLANK_S);
|
||||
psxVBlankStart();
|
||||
|
||||
// Memcard auto ejection - Uses a tick system timed off of real time, decrementing one tick per frame.
|
||||
AutoEject::CountDownTicks();
|
||||
// Memcard IO detection - Uses a tick system to determine when memcards are no longer being written.
|
||||
MemcardBusy::Decrement();
|
||||
|
||||
rcntStartGate(true, sCycle); // Counters Start Gate code
|
||||
if (!GSSMODE1reg.SINT)
|
||||
{
|
||||
hwIntcIrq(INTC_VBLANK_S);
|
||||
rcntStartGate(true, sCycle); // Counters Start Gate code
|
||||
psxVBlankStart();
|
||||
}
|
||||
|
||||
// INTC - VB Blank Start Hack --
|
||||
// Hack fix! This corrects a freezeup in Granda 2 where it decides to spin
|
||||
|
@ -546,6 +543,8 @@ static __fi void VSyncStart(u32 sCycle)
|
|||
static __fi void GSVSync()
|
||||
{
|
||||
// CSR is swapped and GS vBlank IRQ is triggered roughly 3.5 hblanks after VSync Start
|
||||
if (GSSMODE1reg.SINT)
|
||||
return;
|
||||
|
||||
if (IsProgressiveVideoMode())
|
||||
CSRreg.SetField();
|
||||
|
@ -566,15 +565,12 @@ static __fi void VSyncEnd(u32 sCycle)
|
|||
SysTrace.EE.Counters.Write(" ================ EE COUNTER VSYNC END (frame: %d) ================", g_FrameCount);
|
||||
|
||||
g_FrameCount++;
|
||||
|
||||
hwIntcIrq(INTC_VBLANK_E); // HW Irq
|
||||
psxVBlankEnd(); // psxCounters vBlank End
|
||||
rcntEndGate(true, sCycle); // Counters End Gate Code
|
||||
|
||||
// FolderMemoryCard needs information on how much time has passed since the last write
|
||||
// Call it every 60 frames
|
||||
if (!(g_FrameCount % 60))
|
||||
sioNextFrame();
|
||||
if (!GSSMODE1reg.SINT)
|
||||
{
|
||||
hwIntcIrq(INTC_VBLANK_E); // HW Irq
|
||||
psxVBlankEnd(); // psxCounters vBlank End
|
||||
rcntEndGate(true, sCycle); // Counters End Gate Code
|
||||
}
|
||||
|
||||
// This doesn't seem to be needed here. Games only seem to break with regard to the
|
||||
// vsyncstart irq.
|
||||
|
@ -587,76 +583,39 @@ static u32 hsc = 0;
|
|||
static int vblankinc = 0;
|
||||
#endif
|
||||
|
||||
__fi void rcntUpdate_hScanline()
|
||||
{
|
||||
if (!cpuTestCycle(hsyncCounter.sCycle, hsyncCounter.CycleT))
|
||||
return;
|
||||
|
||||
//iopEventAction = 1;
|
||||
if (hsyncCounter.Mode == MODE_HBLANK)
|
||||
{ //HBLANK End / HRENDER Begin
|
||||
rcntEndGate(false, hsyncCounter.sCycle + hsyncCounter.CycleT);
|
||||
psxHBlankEnd();
|
||||
|
||||
// Setup the hRender's start and end cycle information:
|
||||
hsyncCounter.sCycle += vSyncInfo.hBlank; // start (absolute cycle value)
|
||||
hsyncCounter.CycleT = vSyncInfo.hRender; // endpoint (delta from start value)
|
||||
hsyncCounter.Mode = MODE_HRENDER;
|
||||
}
|
||||
else
|
||||
{ //HBLANK START / HRENDER End
|
||||
if (!CSRreg.HSINT)
|
||||
{
|
||||
CSRreg.HSINT = true;
|
||||
if (!GSIMR.HSMSK)
|
||||
gsIrq();
|
||||
}
|
||||
|
||||
rcntStartGate(false, hsyncCounter.sCycle + hsyncCounter.CycleT);
|
||||
psxHBlankStart();
|
||||
|
||||
// set up the hblank's start and end cycle information:
|
||||
hsyncCounter.sCycle += vSyncInfo.hRender; // start (absolute cycle value)
|
||||
hsyncCounter.CycleT = vSyncInfo.hBlank; // endpoint (delta from start value)
|
||||
hsyncCounter.Mode = MODE_HBLANK;
|
||||
|
||||
#ifdef VSYNC_DEBUG
|
||||
hsc++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
__fi void rcntUpdate_vSync()
|
||||
{
|
||||
if (!cpuTestCycle(vsyncCounter.sCycle, vsyncCounter.CycleT))
|
||||
if (!cpuTestCycle(vsyncCounter.startCycle, vsyncCounter.deltaCycles))
|
||||
return;
|
||||
|
||||
if (vsyncCounter.Mode == MODE_VSYNC)
|
||||
if (vsyncCounter.Mode == MODE_VBLANK)
|
||||
{
|
||||
VSyncEnd(vsyncCounter.sCycle + vsyncCounter.CycleT);
|
||||
vsyncCounter.startCycle += vSyncInfo.Blank;
|
||||
vsyncCounter.deltaCycles = vSyncInfo.Render;
|
||||
|
||||
VSyncEnd(vsyncCounter.startCycle);
|
||||
|
||||
vsyncCounter.sCycle += vSyncInfo.Blank;
|
||||
vsyncCounter.CycleT = vSyncInfo.Render;
|
||||
vsyncCounter.Mode = MODE_VRENDER; // VSYNC END - Render begin
|
||||
}
|
||||
else if (vsyncCounter.Mode == MODE_GSBLANK) // GS CSR Swap and interrupt
|
||||
{
|
||||
GSVSync();
|
||||
|
||||
vsyncCounter.Mode = MODE_VSYNC;
|
||||
vsyncCounter.Mode = MODE_VBLANK;
|
||||
// Don't set the start cycle, makes it easier to calculate the correct Vsync End time
|
||||
vsyncCounter.CycleT = vSyncInfo.Blank;
|
||||
vsyncCounter.deltaCycles = vSyncInfo.Blank;
|
||||
}
|
||||
else // VSYNC Start
|
||||
{
|
||||
VSyncStart(vsyncCounter.sCycle + vsyncCounter.CycleT);
|
||||
vsyncCounter.startCycle += vSyncInfo.Render;
|
||||
vsyncCounter.deltaCycles = vSyncInfo.GSBlank;
|
||||
|
||||
VSyncStart(vsyncCounter.startCycle);
|
||||
|
||||
vsyncCounter.sCycle += vSyncInfo.Render;
|
||||
vsyncCounter.CycleT = vSyncInfo.GSBlank;
|
||||
vsyncCounter.Mode = MODE_GSBLANK;
|
||||
|
||||
// Accumulate hsync rounding errors:
|
||||
hsyncCounter.sCycle += vSyncInfo.hSyncError;
|
||||
hsyncCounter.deltaCycles += vSyncInfo.hSyncError;
|
||||
|
||||
#ifdef VSYNC_DEBUG
|
||||
vblankinc++;
|
||||
|
@ -671,6 +630,53 @@ __fi void rcntUpdate_vSync()
|
|||
}
|
||||
}
|
||||
|
||||
__fi void rcntUpdate_hScanline()
|
||||
{
|
||||
if (!cpuTestCycle(hsyncCounter.startCycle, hsyncCounter.deltaCycles))
|
||||
return;
|
||||
|
||||
//iopEventAction = 1;
|
||||
if (hsyncCounter.Mode == MODE_HBLANK)
|
||||
{ //HBLANK End / HRENDER Begin
|
||||
|
||||
// Setup the hRender's start and end cycle information:
|
||||
hsyncCounter.startCycle += vSyncInfo.hBlank; // start (absolute cycle value)
|
||||
hsyncCounter.deltaCycles = vSyncInfo.hRender; // endpoint (delta from start value)
|
||||
if (!GSSMODE1reg.SINT)
|
||||
{
|
||||
rcntEndGate(false, hsyncCounter.startCycle);
|
||||
psxHBlankEnd();
|
||||
}
|
||||
|
||||
hsyncCounter.Mode = MODE_HRENDER;
|
||||
}
|
||||
else
|
||||
{ //HBLANK START / HRENDER End
|
||||
|
||||
// set up the hblank's start and end cycle information:
|
||||
hsyncCounter.startCycle += vSyncInfo.hRender; // start (absolute cycle value)
|
||||
hsyncCounter.deltaCycles = vSyncInfo.hBlank; // endpoint (delta from start value)
|
||||
if (!GSSMODE1reg.SINT)
|
||||
{
|
||||
if (!CSRreg.HSINT)
|
||||
{
|
||||
CSRreg.HSINT = true;
|
||||
if (!GSIMR.HSMSK)
|
||||
gsIrq();
|
||||
}
|
||||
|
||||
rcntStartGate(false, hsyncCounter.startCycle);
|
||||
psxHBlankStart();
|
||||
}
|
||||
|
||||
hsyncCounter.Mode = MODE_HBLANK;
|
||||
|
||||
#ifdef VSYNC_DEBUG
|
||||
hsc++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static __fi void _cpuTestTarget(int i)
|
||||
{
|
||||
if (counters[i].count < counters[i].target)
|
||||
|
@ -726,7 +732,23 @@ __fi bool rcntCanCount(int i)
|
|||
(counters[i].mode.GateSource == 1 && (vsyncCounter.Mode == MODE_VRENDER || counters[i].mode.GateMode != 0)));
|
||||
}
|
||||
|
||||
// forceinline note: this method is called from two locations, but one
|
||||
__fi void rcntSyncCounter(int i)
|
||||
{
|
||||
if (counters[i].mode.ClockSource != 0x3) // don't count hblank sources
|
||||
{
|
||||
const u32 change = (cpuRegs.cycle - counters[i].startCycle) / counters[i].rate;
|
||||
counters[i].startCycle += change * counters[i].rate;
|
||||
|
||||
counters[i].startCycle &= ~(counters[i].rate - 1);
|
||||
|
||||
if (rcntCanCount(i))
|
||||
counters[i].count += change;
|
||||
}
|
||||
else
|
||||
counters[i].startCycle = cpuRegs.cycle;
|
||||
}
|
||||
|
||||
// forceinline note: this method is called from two locations, but one
|
||||
// of them is the interpreter, which doesn't count. ;) So might as
|
||||
// well forceinline it!
|
||||
__fi void rcntUpdate()
|
||||
|
@ -739,22 +761,13 @@ __fi void rcntUpdate()
|
|||
|
||||
for (int i = 0; i <= 3; i++)
|
||||
{
|
||||
if (!rcntCanCount(i))
|
||||
continue;
|
||||
rcntSyncCounter(i);
|
||||
|
||||
if (counters[i].mode.ClockSource != 0x3) // don't count hblank sources
|
||||
{
|
||||
const u32 change = (cpuRegs.cycle - counters[i].sCycleT) / counters[i].rate;
|
||||
counters[i].count += change;
|
||||
counters[i].sCycleT += change * counters[i].rate;
|
||||
if (counters[i].mode.ClockSource == 0x3 || !rcntCanCount(i)) // don't count hblank sources
|
||||
continue;
|
||||
|
||||
// Check Counter Targets and Overflows:
|
||||
// Check Overflow first, in case the target is 0
|
||||
_cpuTestOverflow(i);
|
||||
_cpuTestTarget(i);
|
||||
}
|
||||
else
|
||||
counters[i].sCycleT = cpuRegs.cycle;
|
||||
_cpuTestOverflow(i);
|
||||
_cpuTestTarget(i);
|
||||
}
|
||||
|
||||
cpuRcntSet();
|
||||
|
@ -784,10 +797,8 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
|
|||
// Update counters using the hblank as the clock. This keeps the hblank source
|
||||
// nicely in sync with the counters and serves as an optimization also, since these
|
||||
// counter won't receive special rcntUpdate scheduling.
|
||||
|
||||
// Note: Target and overflow tests must be done here since they won't be done
|
||||
// currectly by rcntUpdate (since it's not being scheduled for these counters)
|
||||
|
||||
counters[i].count += HBLANK_COUNTER_SPEED;
|
||||
_cpuTestOverflow(i);
|
||||
_cpuTestTarget(i);
|
||||
|
@ -803,11 +814,10 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
|
|||
{
|
||||
case 0x0: //Count When Signal is low (V_RENDER ONLY)
|
||||
|
||||
// Just set the start cycle (sCycleT) -- counting will be done as needed
|
||||
// Just set the start cycle -- counting will be done as needed
|
||||
// for events (overflows, targets, mode changes, and the gate off below)
|
||||
|
||||
counters[i].count = rcntRcount(i);
|
||||
counters[i].sCycleT = sCycle;
|
||||
rcntSyncCounter(i);
|
||||
counters[i].startCycle = sCycle & ~(counters[i].rate - 1);
|
||||
EECNT_LOG("EE Counter[%d] %s StartGate Type0, count = %x", i,
|
||||
isVblank ? "vblank" : "hblank", counters[i].count);
|
||||
break;
|
||||
|
@ -816,14 +826,12 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
|
|||
break;
|
||||
case 0x1: // Reset on Vsync start
|
||||
case 0x3: // Reset on Vsync start and end
|
||||
{
|
||||
rcntSyncCounter(i);
|
||||
counters[i].count = 0;
|
||||
counters[i].target &= 0xffff;
|
||||
const u32 change = (sCycle - counters[i].sCycleT) / counters[i].rate;
|
||||
counters[i].sCycleT = sCycle - ((sCycle - counters[i].sCycleT) - (change * counters[i].rate));
|
||||
counters[i].startCycle = sCycle & ~(counters[i].rate - 1);
|
||||
EECNT_LOG("EE Counter[%d] %s StartGate Type%d, count = %x", i,
|
||||
isVblank ? "vblank" : "hblank", counters[i].mode.GateMode, counters[i].count);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -850,7 +858,7 @@ static __fi void rcntEndGate(bool isVblank, u32 sCycle)
|
|||
switch (counters[i].mode.GateMode)
|
||||
{
|
||||
case 0x0: //Count When Signal is low (V_RENDER ONLY)
|
||||
counters[i].sCycleT = sCycle;
|
||||
counters[i].startCycle = sCycle & ~(counters[i].rate - 1);
|
||||
|
||||
EECNT_LOG("EE Counter[%d] %s EndGate Type0, count = %x", i,
|
||||
isVblank ? "vblank" : "hblank", counters[i].count);
|
||||
|
@ -862,14 +870,12 @@ static __fi void rcntEndGate(bool isVblank, u32 sCycle)
|
|||
|
||||
case 0x2: // Reset on Vsync end
|
||||
case 0x3: // Reset on Vsync start and end
|
||||
{
|
||||
rcntSyncCounter(i);
|
||||
EECNT_LOG("EE Counter[%d] %s EndGate Type%d, count = %x", i,
|
||||
isVblank ? "vblank" : "hblank", counters[i].mode.GateMode, counters[i].count);
|
||||
counters[i].count = 0;
|
||||
counters[i].target &= 0xffff;
|
||||
const u32 change = (sCycle - counters[i].sCycleT) / counters[i].rate;
|
||||
counters[i].sCycleT = sCycle - ((sCycle - counters[i].sCycleT) - (change * counters[i].rate));
|
||||
}
|
||||
counters[i].startCycle = sCycle & ~(counters[i].rate - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -879,14 +885,7 @@ static __fi void rcntEndGate(bool isVblank, u32 sCycle)
|
|||
|
||||
static __fi void rcntWmode(int index, u32 value)
|
||||
{
|
||||
if (rcntCanCount(index) && counters[index].mode.ClockSource != 0x3)
|
||||
{
|
||||
const u32 change = (cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate;
|
||||
counters[index].count += change;
|
||||
counters[index].sCycleT += change * counters[index].rate;
|
||||
}
|
||||
else
|
||||
counters[index].sCycleT = cpuRegs.cycle;
|
||||
rcntSyncCounter(index);
|
||||
|
||||
// Clear OverflowReached and TargetReached flags (0xc00 mask), but *only* if they are set to 1 in the
|
||||
// given value. (yes, the bits are cleared when written with '1's).
|
||||
|
@ -902,6 +901,8 @@ static __fi void rcntWmode(int index, u32 value)
|
|||
case 3: counters[index].rate = vSyncInfo.hBlank+vSyncInfo.hRender; break;
|
||||
}
|
||||
|
||||
// In case the rate has changed we need to set the start cycle to the previous tick.
|
||||
counters[index].startCycle = cpuRegs.cycle & ~(counters[index].rate - 1);
|
||||
_rcntSetGate(index);
|
||||
_rcntSet(index);
|
||||
}
|
||||
|
@ -911,13 +912,7 @@ static __fi void rcntWcount(int index, u32 value)
|
|||
EECNT_LOG("EE Counter[%d] writeCount = %x, oldcount=%x, target=%x", index, value, counters[index].count, counters[index].target);
|
||||
|
||||
// re-calculate the start cycle of the counter based on elapsed time since the last counter update:
|
||||
if (rcntCanCount(index) && counters[index].mode.ClockSource != 0x3)
|
||||
{
|
||||
const u32 change = (cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate;
|
||||
counters[index].sCycleT += change * counters[index].rate;
|
||||
}
|
||||
else
|
||||
counters[index].sCycleT = cpuRegs.cycle;
|
||||
rcntSyncCounter(index);
|
||||
|
||||
counters[index].count = value & 0xffff;
|
||||
|
||||
|
@ -940,14 +935,7 @@ static __fi void rcntWtarget(int index, u32 value)
|
|||
// If the target is behind the current count, set it up so that the counter must
|
||||
// overflow first before the target fires:
|
||||
|
||||
if (rcntCanCount(index) && counters[index].mode.ClockSource != 0x3)
|
||||
{
|
||||
const u32 change = (cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate;
|
||||
counters[index].count += change;
|
||||
counters[index].sCycleT += change * counters[index].rate;
|
||||
}
|
||||
else
|
||||
counters[index].sCycleT = cpuRegs.cycle;
|
||||
rcntSyncCounter(index);
|
||||
|
||||
if (counters[index].target <= counters[index].count)
|
||||
counters[index].target |= EECNT_FUTURE_TARGET;
|
||||
|
@ -965,11 +953,9 @@ __fi u32 rcntRcount(int index)
|
|||
{
|
||||
u32 ret;
|
||||
|
||||
// only count if the counter is turned on (0x80) and is not an hsync gate (!0x03)
|
||||
if (rcntCanCount(index) && (counters[index].mode.ClockSource != 0x3))
|
||||
ret = counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate);
|
||||
else
|
||||
ret = counters[index].count;
|
||||
rcntSyncCounter(index);
|
||||
|
||||
ret = counters[index].count;
|
||||
|
||||
// Spams the Console.
|
||||
EECNT_LOG("EE Counter[%d] readCount32 = %x", index, ret);
|
||||
|
@ -984,24 +970,25 @@ __fi u16 rcntRead32(u32 mem)
|
|||
// are all fixed to 0, so we always truncate everything in these two pages using a u16
|
||||
// return value! --air
|
||||
|
||||
switch( mem ) {
|
||||
case(RCNT0_COUNT): return (u16)rcntRcount(0);
|
||||
case(RCNT0_MODE): return (u16)counters[0].modeval;
|
||||
case(RCNT0_TARGET): return (u16)counters[0].target;
|
||||
case(RCNT0_HOLD): return (u16)counters[0].hold;
|
||||
switch( mem )
|
||||
{
|
||||
case(RCNT0_COUNT): return (u16)rcntRcount(0);
|
||||
case(RCNT0_MODE): return (u16)counters[0].modeval;
|
||||
case(RCNT0_TARGET): return (u16)counters[0].target;
|
||||
case(RCNT0_HOLD): return (u16)counters[0].hold;
|
||||
|
||||
case(RCNT1_COUNT): return (u16)rcntRcount(1);
|
||||
case(RCNT1_MODE): return (u16)counters[1].modeval;
|
||||
case(RCNT1_TARGET): return (u16)counters[1].target;
|
||||
case(RCNT1_HOLD): return (u16)counters[1].hold;
|
||||
case(RCNT1_COUNT): return (u16)rcntRcount(1);
|
||||
case(RCNT1_MODE): return (u16)counters[1].modeval;
|
||||
case(RCNT1_TARGET): return (u16)counters[1].target;
|
||||
case(RCNT1_HOLD): return (u16)counters[1].hold;
|
||||
|
||||
case(RCNT2_COUNT): return (u16)rcntRcount(2);
|
||||
case(RCNT2_MODE): return (u16)counters[2].modeval;
|
||||
case(RCNT2_TARGET): return (u16)counters[2].target;
|
||||
case(RCNT2_COUNT): return (u16)rcntRcount(2);
|
||||
case(RCNT2_MODE): return (u16)counters[2].modeval;
|
||||
case(RCNT2_TARGET): return (u16)counters[2].target;
|
||||
|
||||
case(RCNT3_COUNT): return (u16)rcntRcount(3);
|
||||
case(RCNT3_MODE): return (u16)counters[3].modeval;
|
||||
case(RCNT3_TARGET): return (u16)counters[3].target;
|
||||
case(RCNT3_COUNT): return (u16)rcntRcount(3);
|
||||
case(RCNT3_MODE): return (u16)counters[3].modeval;
|
||||
case(RCNT3_TARGET): return (u16)counters[3].target;
|
||||
}
|
||||
|
||||
return psHu16(mem);
|
||||
|
@ -1016,24 +1003,25 @@ __fi bool rcntWrite32(u32 mem, mem32_t& value)
|
|||
// count, mode, target, and hold. This will allow for a simplified handler for register
|
||||
// reads.
|
||||
|
||||
switch( mem ) {
|
||||
case(RCNT0_COUNT): return rcntWcount(0, value), false;
|
||||
case(RCNT0_MODE): return rcntWmode(0, value), false;
|
||||
case(RCNT0_TARGET): return rcntWtarget(0, value), false;
|
||||
case(RCNT0_HOLD): return rcntWhold(0, value), false;
|
||||
switch( mem )
|
||||
{
|
||||
case(RCNT0_COUNT): return rcntWcount(0, value), false;
|
||||
case(RCNT0_MODE): return rcntWmode(0, value), false;
|
||||
case(RCNT0_TARGET): return rcntWtarget(0, value), false;
|
||||
case(RCNT0_HOLD): return rcntWhold(0, value), false;
|
||||
|
||||
case(RCNT1_COUNT): return rcntWcount(1, value), false;
|
||||
case(RCNT1_MODE): return rcntWmode(1, value), false;
|
||||
case(RCNT1_TARGET): return rcntWtarget(1, value), false;
|
||||
case(RCNT1_HOLD): return rcntWhold(1, value), false;
|
||||
case(RCNT1_COUNT): return rcntWcount(1, value), false;
|
||||
case(RCNT1_MODE): return rcntWmode(1, value), false;
|
||||
case(RCNT1_TARGET): return rcntWtarget(1, value), false;
|
||||
case(RCNT1_HOLD): return rcntWhold(1, value), false;
|
||||
|
||||
case(RCNT2_COUNT): return rcntWcount(2, value), false;
|
||||
case(RCNT2_MODE): return rcntWmode(2, value), false;
|
||||
case(RCNT2_TARGET): return rcntWtarget(2, value), false;
|
||||
case(RCNT2_COUNT): return rcntWcount(2, value), false;
|
||||
case(RCNT2_MODE): return rcntWmode(2, value), false;
|
||||
case(RCNT2_TARGET): return rcntWtarget(2, value), false;
|
||||
|
||||
case(RCNT3_COUNT): return rcntWcount(3, value), false;
|
||||
case(RCNT3_MODE): return rcntWmode(3, value), false;
|
||||
case(RCNT3_TARGET): return rcntWtarget(3, value), false;
|
||||
case(RCNT3_COUNT): return rcntWcount(3, value), false;
|
||||
case(RCNT3_MODE): return rcntWmode(3, value), false;
|
||||
case(RCNT3_TARGET): return rcntWtarget(3, value), false;
|
||||
}
|
||||
|
||||
// unhandled .. do memory writeback.
|
||||
|
@ -1051,8 +1039,8 @@ bool SaveStateBase::rcntFreeze()
|
|||
Freeze(counters);
|
||||
Freeze(hsyncCounter);
|
||||
Freeze(vsyncCounter);
|
||||
Freeze(nextCounter);
|
||||
Freeze(nextsCounter);
|
||||
Freeze(nextDeltaCounter);
|
||||
Freeze(nextStartCounter);
|
||||
Freeze(vSyncInfo);
|
||||
Freeze(gsVideoMode);
|
||||
Freeze(gsIsInterlaced);
|
||||
|
|
|
@ -60,14 +60,14 @@ struct Counter
|
|||
};
|
||||
u32 target, hold;
|
||||
u32 rate, interrupt;
|
||||
u32 sCycleT; // delta values should be signed.
|
||||
u32 startCycle; // delta values should be signed.
|
||||
};
|
||||
|
||||
struct SyncCounter
|
||||
{
|
||||
u32 Mode;
|
||||
u32 sCycle; // start cycle of timer
|
||||
s32 CycleT;
|
||||
u32 startCycle; // start cycle of timer
|
||||
s32 deltaCycles;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
@ -107,9 +107,6 @@ struct SyncCounter
|
|||
#define MODE_VRENDER 0x0 //Set during the Render/Frame Scanlines
|
||||
#define MODE_VBLANK 0x1 //Set during the Blanking Scanlines
|
||||
#define MODE_GSBLANK 0x2 //Set during the Syncing Scanlines (Delayed GS CSR Swap)
|
||||
#define MODE_VSYNC 0x3 //Set during the Syncing Scanlines
|
||||
#define MODE_VBLANK1 0x0 //Set during the Blanking Scanlines (half-frame 1)
|
||||
#define MODE_VBLANK2 0x1 //Set during the Blanking Scanlines (half-frame 2)
|
||||
|
||||
#define MODE_HRENDER 0x0 //Set for ~5/6 of 1 Scanline
|
||||
#define MODE_HBLANK 0x1 //Set for the remaining ~1/6 of 1 Scanline
|
||||
|
@ -120,13 +117,14 @@ extern Counter counters[4];
|
|||
extern SyncCounter hsyncCounter;
|
||||
extern SyncCounter vsyncCounter;
|
||||
|
||||
extern s32 nextCounter; // delta until the next counter event (must be signed)
|
||||
extern u32 nextsCounter;
|
||||
extern s32 nextDeltaCounter; // delta until the next counter event (must be signed)
|
||||
extern u32 nextStartCounter;
|
||||
extern uint g_FrameCount;
|
||||
|
||||
extern void rcntUpdate_hScanline();
|
||||
extern void rcntUpdate_vSync();
|
||||
extern bool rcntCanCount(int i);
|
||||
extern void rcntSyncCounter(int i);
|
||||
extern void rcntUpdate();
|
||||
|
||||
extern void rcntInit();
|
||||
|
|
12
pcsx2/GS.cpp
12
pcsx2/GS.cpp
|
@ -52,6 +52,7 @@ static __fi void gsCSRwrite( const tGS_CSR& csr )
|
|||
|
||||
if(csr.SIGNAL)
|
||||
{
|
||||
const bool resume = CSRreg.SIGNAL;
|
||||
// SIGNAL : What's not known here is whether or not the SIGID register should be updated
|
||||
// here or when the IMR is cleared (below).
|
||||
GUNIT_LOG("csr.SIGNAL");
|
||||
|
@ -65,7 +66,9 @@ static __fi void gsCSRwrite( const tGS_CSR& csr )
|
|||
}
|
||||
else CSRreg.SIGNAL = false;
|
||||
gifUnit.gsSIGNAL.queued = false;
|
||||
gifUnit.Execute(false, true); // Resume paused transfers
|
||||
|
||||
if (resume)
|
||||
gifUnit.Execute(false, true); // Resume paused transfers
|
||||
}
|
||||
|
||||
if (csr.FINISH) {
|
||||
|
@ -179,14 +182,17 @@ void gsWrite64_generic( u32 mem, u64 value )
|
|||
void gsWrite64_page_00( u32 mem, u64 value )
|
||||
{
|
||||
s_GSRegistersWritten |= (mem == GS_DISPFB1 || mem == GS_DISPFB2 || mem == GS_PMODE);
|
||||
|
||||
bool reqUpdate = false;
|
||||
if (mem == GS_SMODE1 || mem == GS_SMODE2)
|
||||
{
|
||||
if (value != *(u64*)PS2GS_BASE(mem))
|
||||
UpdateVSyncRate(false);
|
||||
reqUpdate = true;
|
||||
}
|
||||
|
||||
gsWrite64_generic( mem, value );
|
||||
|
||||
if (reqUpdate)
|
||||
UpdateVSyncRate(false);
|
||||
}
|
||||
|
||||
void gsWrite64_page_01( u32 mem, u64 value )
|
||||
|
|
40
pcsx2/GS.h
40
pcsx2/GS.h
|
@ -6,6 +6,7 @@
|
|||
#include "Common.h"
|
||||
#include "Gif.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/GSRegs.h"
|
||||
|
||||
#include "common/SingleRegisterTypes.h"
|
||||
|
||||
|
@ -193,42 +194,6 @@ union tGS_IMR
|
|||
bool masked() const { return (SIGMSK || FINISHMSK || HSMSK || VSMSK || EDWMSK); }
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// GSRegSMODE1
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Previously, the union was used to get the CMOD bit of the SMODE1 register
|
||||
// Commenting it out as it's unused right now. (Might potentially be useful in the future)
|
||||
//union GSRegSMODE1
|
||||
//{
|
||||
// struct
|
||||
// {
|
||||
// u32 RC : 3;
|
||||
// u32 LC : 7;
|
||||
// u32 T1248 : 2;
|
||||
// u32 SLCK : 1;
|
||||
// u32 CMOD : 2;
|
||||
// u32 EX : 1;
|
||||
// u32 PRST : 1;
|
||||
// u32 SINT : 1;
|
||||
// u32 XPCK : 1;
|
||||
// u32 PCK2 : 2;
|
||||
// u32 SPML : 4;
|
||||
// u32 GCONT : 1;
|
||||
// u32 PHS : 1;
|
||||
// u32 PVS : 1;
|
||||
// u32 PEHS : 1;
|
||||
// u32 PEVS : 1;
|
||||
// u32 CLKSEL : 2;
|
||||
// u32 NVCK : 1;
|
||||
// u32 SLCK2 : 1;
|
||||
// u32 VCKSEL : 2;
|
||||
// u32 VHP : 1;
|
||||
// u32 _PAD1 : 27;
|
||||
// };
|
||||
//
|
||||
// u64 SMODE1;
|
||||
//};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// GSRegSIGBLID
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -242,7 +207,7 @@ struct GSRegSIGBLID
|
|||
#define PS2GS_BASE(mem) (PS2MEM_GS + (mem & 0x13ff))
|
||||
|
||||
#define CSRreg ((tGS_CSR&)*(PS2MEM_GS + 0x1000))
|
||||
|
||||
#define GSSMODE1reg ((GSRegSMODE1&)*(PS2MEM_GS + 0x0010))
|
||||
#define GSCSRr ((u32&)*(PS2MEM_GS + 0x1000))
|
||||
#define GSIMR ((tGS_IMR&)*(PS2MEM_GS + 0x1010))
|
||||
#define GSSIGLBLID ((GSRegSIGBLID&)*(PS2MEM_GS + 0x1080))
|
||||
|
@ -264,6 +229,7 @@ enum class GS_VideoMode : int
|
|||
};
|
||||
|
||||
extern GS_VideoMode gsVideoMode;
|
||||
extern u32 lastCSRFlag;
|
||||
extern bool gsIsInterlaced;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
#define PSXSOUNDCLK ((int)(48000))
|
||||
|
||||
psxCounter psxCounters[NUM_COUNTERS];
|
||||
s32 psxNextCounter;
|
||||
u32 psxNextsCounter;
|
||||
s32 psxNextDeltaCounter;
|
||||
u32 psxNextStartCounter;
|
||||
|
||||
bool hBlanking = false;
|
||||
bool vBlanking = false;
|
||||
|
@ -44,44 +44,27 @@ bool vBlanking = false;
|
|||
// used to disable targets until after an overflow
|
||||
#define IOPCNT_FUTURE_TARGET (0x1000000000ULL)
|
||||
#define IOPCNT_MODE_WRITE_MSK 0x63FF
|
||||
#define IOPCNT_MODE_FLAG_MSK 0x1800
|
||||
#define IOPCNT_MODE_FLAG_MSK 0x1C00
|
||||
|
||||
#define IOPCNT_ENABLE_GATE (1 << 0) // enables gate-based counters
|
||||
#define IOPCNT_MODE_GATE (3 << 1) // 0x6 Gate mode (dependant on counter)
|
||||
#define IOPCNT_MODE_RESET_CNT (1 << 3) // 0x8 resets the counter on target (if interrupt only?)
|
||||
#define IOPCNT_INT_TARGET (1 << 4) // 0x10 triggers an interrupt on targets
|
||||
#define IOPCNT_INT_OVERFLOW (1 << 5) // 0x20 triggers an interrupt on overflows
|
||||
#define IOPCNT_INT_REPEAT (1 << 6) // 0x40 0=One shot (ignore TOGGLE bit 7) 1=Repeat Fire (Check TOGGLE bit 7)
|
||||
#define IOPCNT_INT_TOGGLE (1 << 7) // 0x80 0=Pulse (reset on read), 1=toggle each interrupt condition (in 1 shot not reset after fired)
|
||||
#define IOPCNT_ALT_SOURCE (1 << 8) // 0x100 uses hblank on counters 1 and 3, and PSXCLOCK on counter 0
|
||||
#define IOPCNT_INT_REQ (1 << 10) // 0x400 1=Can fire interrupt, 0=Interrupt Fired (reset on read if not 1 shot)
|
||||
#define IOPCNT_INT_CMPFLAG (1 << 11) // 0x800 1=Target interrupt raised
|
||||
#define IOPCNT_INT_OFLWFLAG (1 << 12) // 0x1000 1=Overflow interrupt raised
|
||||
#define IOPCNT_GATE_CNT_LOW 0 // Counts only when signal is low (V/H RENDER).
|
||||
#define IOPCNT_GATE_CLR_END 1 // Counts continuously, clears at end of BLANK.
|
||||
#define IOPCNT_GATE_CNT_HIGH_ZERO_OFF 2 // Starts at beginning of next BLANK, counts only during BLANK, returns zero any other time.
|
||||
#define IOPCNT_GATE_START_AT_END 3 // Starts at end of next BLANK, continuous count, no clear.
|
||||
|
||||
// Use an arbitrary value to flag HBLANK counters.
|
||||
// These counters will be counted by the hblank gates coming from the EE,
|
||||
// which ensures they stay 100% in sync with the EE's hblank counters.
|
||||
#define PSXHBLANK 0x2001
|
||||
|
||||
#if 0
|
||||
// Unused
|
||||
static void psxRcntReset(int index)
|
||||
{
|
||||
psxCounters[index].count = 0;
|
||||
psxCounters[index].mode &= ~0x18301C00;
|
||||
psxCounters[index].sCycleT = psxRegs.cycle;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool psxRcntCanCount(int cntidx)
|
||||
{
|
||||
if (psxCounters[cntidx].mode & IOPCNT_STOPPED)
|
||||
if (psxCounters[cntidx].mode.stopped)
|
||||
return false;
|
||||
|
||||
if (!(psxCounters[cntidx].mode & IOPCNT_ENABLE_GATE))
|
||||
if (!(psxCounters[cntidx].mode.gateEnable))
|
||||
return true;
|
||||
|
||||
const u32 gateMode = (psxCounters[cntidx].mode & IOPCNT_MODE_GATE) >> 1;
|
||||
const u32 gateMode = psxCounters[cntidx].mode.gateMode;
|
||||
|
||||
if (cntidx == 2 || cntidx == 4 || cntidx == 5)
|
||||
{
|
||||
|
@ -92,13 +75,40 @@ static bool psxRcntCanCount(int cntidx)
|
|||
const bool blanking = cntidx == 0 ? hBlanking : vBlanking;
|
||||
|
||||
// Stop counting if Gate mode 0 (only count when rendering) and blanking or Gate mode 2 (only count when blanking) when not blanking
|
||||
if ((gateMode == 0 && blanking == true) || (gateMode == 2 && blanking == false))
|
||||
if ((gateMode == IOPCNT_GATE_CNT_LOW && blanking == true) || (gateMode == IOPCNT_GATE_CNT_HIGH_ZERO_OFF && blanking == false))
|
||||
return false;
|
||||
|
||||
// All other cases allow counting.
|
||||
return true;
|
||||
}
|
||||
|
||||
static void psxRcntSync(int cntidx)
|
||||
{
|
||||
if ((psxCounters[cntidx].currentIrqMode.repeatInterrupt) && !(psxCounters[cntidx].currentIrqMode.toggleInterrupt))
|
||||
{ //Repeat IRQ mode Pulsed, resets a few cycles after the interrupt, this should do.
|
||||
psxCounters[cntidx].mode.intrEnable = true;
|
||||
}
|
||||
|
||||
if (psxRcntCanCount(cntidx) && psxCounters[cntidx].rate != PSXHBLANK)
|
||||
{
|
||||
const u32 change = (psxRegs.cycle - psxCounters[cntidx].startCycle) / psxCounters[cntidx].rate;
|
||||
if (change > 0)
|
||||
{
|
||||
psxCounters[cntidx].count += change;
|
||||
psxCounters[cntidx].startCycle += change * psxCounters[cntidx].rate;
|
||||
|
||||
psxCounters[cntidx].startCycle &= ~(psxCounters[cntidx].rate - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (psxCounters[cntidx].mode.gateEnable && psxCounters[cntidx].mode.gateMode == IOPCNT_GATE_CNT_HIGH_ZERO_OFF)
|
||||
psxCounters[cntidx].count = 0;
|
||||
|
||||
psxCounters[cntidx].startCycle = psxRegs.cycle;
|
||||
}
|
||||
}
|
||||
|
||||
static void _rcntSet(int cntidx)
|
||||
{
|
||||
u64 overflowCap = (cntidx >= 3) ? 0x100000000ULL : 0x10000;
|
||||
|
@ -114,36 +124,33 @@ static void _rcntSet(int cntidx)
|
|||
if (counter.rate == PSXHBLANK || !psxRcntCanCount(cntidx))
|
||||
return;
|
||||
|
||||
if (!(counter.mode & (IOPCNT_INT_TARGET | IOPCNT_INT_OVERFLOW)))
|
||||
return;
|
||||
// check for special cases where the overflow or target has just passed
|
||||
// (we probably missed it because we're doing/checking other things)
|
||||
if (counter.count > overflowCap || counter.count > counter.target)
|
||||
{
|
||||
psxNextCounter = 4;
|
||||
psxNextDeltaCounter = 4;
|
||||
return;
|
||||
}
|
||||
|
||||
c = (u64)((overflowCap - counter.count) * counter.rate) - (psxRegs.cycle - counter.sCycleT);
|
||||
c += psxRegs.cycle - psxNextsCounter; // adjust for time passed since last rcntUpdate();
|
||||
c = (u64)((overflowCap - counter.count) * counter.rate) - (psxRegs.cycle - counter.startCycle);
|
||||
c += psxRegs.cycle - psxNextStartCounter; // adjust for time passed since last rcntUpdate();
|
||||
|
||||
if (c < (u64)psxNextCounter)
|
||||
if (c < (u64)psxNextDeltaCounter)
|
||||
{
|
||||
psxNextCounter = (u32)c;
|
||||
psxSetNextBranch(psxNextsCounter, psxNextCounter); //Need to update on counter resets/target changes
|
||||
psxNextDeltaCounter = (u32)c;
|
||||
psxSetNextBranch(psxNextStartCounter, psxNextDeltaCounter); //Need to update on counter resets/target changes
|
||||
}
|
||||
|
||||
//if((counter.mode & 0x10) == 0 || psxCounters[i].target > 0xffff) continue;
|
||||
if (counter.target & IOPCNT_FUTURE_TARGET)
|
||||
return;
|
||||
|
||||
c = (s64)((counter.target - counter.count) * counter.rate) - (psxRegs.cycle - counter.sCycleT);
|
||||
c += psxRegs.cycle - psxNextsCounter; // adjust for time passed since last rcntUpdate();
|
||||
c = (s64)((counter.target - counter.count) * counter.rate) - (psxRegs.cycle - counter.startCycle);
|
||||
c += psxRegs.cycle - psxNextStartCounter; // adjust for time passed since last rcntUpdate();
|
||||
|
||||
if (c < (u64)psxNextCounter)
|
||||
if (c < (u64)psxNextDeltaCounter)
|
||||
{
|
||||
psxNextCounter = (u32)c;
|
||||
psxSetNextBranch(psxNextsCounter, psxNextCounter); //Need to update on counter resets/target changes
|
||||
psxNextDeltaCounter = (u32)c;
|
||||
psxSetNextBranch(psxNextStartCounter, psxNextDeltaCounter); //Need to update on counter resets/target changes
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,14 +164,18 @@ void psxRcntInit()
|
|||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
psxCounters[i].rate = 1;
|
||||
psxCounters[i].mode |= IOPCNT_INT_REQ;
|
||||
psxCounters[i].mode.intrEnable = true;
|
||||
psxCounters[i].target = IOPCNT_FUTURE_TARGET;
|
||||
psxCounters[i].currentIrqMode.repeatInterrupt = false;
|
||||
psxCounters[i].currentIrqMode.toggleInterrupt = false;
|
||||
}
|
||||
for (i = 3; i < 6; i++)
|
||||
{
|
||||
psxCounters[i].rate = 1;
|
||||
psxCounters[i].mode |= IOPCNT_INT_REQ;
|
||||
psxCounters[i].mode.intrEnable = true;
|
||||
psxCounters[i].target = IOPCNT_FUTURE_TARGET;
|
||||
psxCounters[i].currentIrqMode.repeatInterrupt = false;
|
||||
psxCounters[i].currentIrqMode.toggleInterrupt = false;
|
||||
}
|
||||
|
||||
psxCounters[0].interrupt = 0x10;
|
||||
|
@ -176,72 +187,71 @@ void psxRcntInit()
|
|||
psxCounters[5].interrupt = 0x10000;
|
||||
|
||||
psxCounters[6].rate = 768;
|
||||
psxCounters[6].CycleT = psxCounters[6].rate;
|
||||
psxCounters[6].mode = 0x8;
|
||||
psxCounters[6].deltaCycles = psxCounters[6].rate;
|
||||
psxCounters[6].mode.modeval = 0x8;
|
||||
|
||||
psxCounters[7].rate = PSXCLK / 1000;
|
||||
psxCounters[7].CycleT = psxCounters[7].rate;
|
||||
psxCounters[7].mode = 0x8;
|
||||
psxCounters[7].deltaCycles = psxCounters[7].rate;
|
||||
psxCounters[7].mode.modeval = 0x8;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
psxCounters[i].startCycle = psxRegs.cycle;
|
||||
|
||||
// Tell the IOP to branch ASAP, so that timers can get
|
||||
// configured properly.
|
||||
psxNextCounter = 1;
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
psxNextDeltaCounter = 1;
|
||||
psxNextStartCounter = psxRegs.cycle;
|
||||
}
|
||||
|
||||
static bool _rcntFireInterrupt(int i, bool isOverflow)
|
||||
static void _rcntFireInterrupt(int i, bool isOverflow)
|
||||
{
|
||||
bool ret = false;
|
||||
#
|
||||
if (psxCounters[i].mode & IOPCNT_INT_REQ)
|
||||
bool updateIntr = psxCounters[i].currentIrqMode.repeatInterrupt;
|
||||
|
||||
if (psxCounters[i].mode.intrEnable)
|
||||
{
|
||||
// IRQ fired
|
||||
PSXCNT_LOG("Counter %d %s IRQ Fired count %x", i, isOverflow ? "Overflow" : "Target", psxCounters[i].count);
|
||||
psxHu32(0x1070) |= psxCounters[i].interrupt;
|
||||
iopTestIntc();
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//DevCon.Warning("Counter %d IRQ not fired count %x", i, psxCounters[i].count);
|
||||
if (!(psxCounters[i].mode & IOPCNT_INT_REPEAT)) // One shot
|
||||
bool already_set = isOverflow ? psxCounters[i].mode.overflowFlag : psxCounters[i].mode.targetFlag;
|
||||
if (updateIntr || !already_set)
|
||||
{
|
||||
PSXCNT_LOG("Counter %x ignoring %s interrupt (One Shot)", i, isOverflow ? "Overflow" : "Target");
|
||||
return false;
|
||||
// IRQ fired
|
||||
//DevCon.Warning("Counter %d %s IRQ Fired count %x target %x psx Cycle %d", i, isOverflow ? "Overflow" : "Target", psxCounters[i].count, psxCounters[i].target, psxRegs.cycle);
|
||||
psxHu32(0x1070) |= psxCounters[i].interrupt;
|
||||
iopTestIntc();
|
||||
}
|
||||
|
||||
updateIntr |= psxCounters[i].currentIrqMode.toggleInterrupt;
|
||||
}
|
||||
|
||||
if (updateIntr)
|
||||
{
|
||||
if (psxCounters[i].currentIrqMode.toggleInterrupt)
|
||||
{
|
||||
// Toggle mode
|
||||
psxCounters[i].mode.intrEnable ^= true; // Interrupt flag inverted
|
||||
}
|
||||
else
|
||||
{
|
||||
psxCounters[i].mode.intrEnable = false; // Interrupt flag set low
|
||||
}
|
||||
}
|
||||
|
||||
if (psxCounters[i].mode & IOPCNT_INT_TOGGLE)
|
||||
{
|
||||
// Toggle mode
|
||||
psxCounters[i].mode ^= IOPCNT_INT_REQ; // Interrupt flag inverted
|
||||
}
|
||||
else
|
||||
{
|
||||
psxCounters[i].mode &= ~IOPCNT_INT_REQ; // Interrupt flag set low
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _rcntTestTarget(int i)
|
||||
{
|
||||
if (psxCounters[i].count < psxCounters[i].target)
|
||||
return;
|
||||
|
||||
PSXCNT_LOG("IOP Counter[%d] target 0x%I64x >= 0x%I64x (mode: %x)",
|
||||
i, psxCounters[i].count, psxCounters[i].target, psxCounters[i].mode);
|
||||
i, psxCounters[i].count, psxCounters[i].target, psxCounters[i].mode.modeval);
|
||||
|
||||
if (psxCounters[i].mode & IOPCNT_INT_TARGET)
|
||||
if (psxCounters[i].mode.targetIntr)
|
||||
{
|
||||
// Target interrupt
|
||||
if (_rcntFireInterrupt(i, false))
|
||||
psxCounters[i].mode |= IOPCNT_INT_CMPFLAG;
|
||||
_rcntFireInterrupt(i, false);
|
||||
}
|
||||
|
||||
if (psxCounters[i].mode & IOPCNT_MODE_RESET_CNT)
|
||||
psxCounters[i].mode.targetFlag = true;
|
||||
|
||||
if (psxCounters[i].mode.zeroReturn)
|
||||
{
|
||||
// Reset on target
|
||||
psxCounters[i].count -= psxCounters[i].target;
|
||||
|
@ -258,20 +268,20 @@ static __fi void _rcntTestOverflow(int i)
|
|||
return;
|
||||
|
||||
PSXCNT_LOG("IOP Counter[%d] overflow 0x%I64x >= 0x%I64x (mode: %x)",
|
||||
i, psxCounters[i].count, maxTarget, psxCounters[i].mode);
|
||||
i, psxCounters[i].count, maxTarget, psxCounters[i].mode.modeval);
|
||||
|
||||
if ((psxCounters[i].mode & IOPCNT_INT_OVERFLOW))
|
||||
if (psxCounters[i].mode.overflIntr)
|
||||
{
|
||||
// Overflow interrupt
|
||||
if (_rcntFireInterrupt(i, true))
|
||||
psxCounters[i].mode |= IOPCNT_INT_OFLWFLAG; // Overflow flag
|
||||
_rcntFireInterrupt(i, true);
|
||||
}
|
||||
|
||||
psxCounters[i].mode.overflowFlag = true;
|
||||
|
||||
// Update count.
|
||||
// Count wraps around back to zero, while the target is restored (if not in one shot mode).
|
||||
// (high bit of the target gets set by rcntWtarget when the target is behind
|
||||
// the counter value, and thus should not be flagged until after an overflow)
|
||||
|
||||
psxCounters[i].count -= maxTarget + 1;
|
||||
psxCounters[i].target &= maxTarget;
|
||||
}
|
||||
|
@ -314,10 +324,10 @@ Gate:
|
|||
|
||||
static void _psxCheckStartGate(int i)
|
||||
{
|
||||
if (!(psxCounters[i].mode & IOPCNT_ENABLE_GATE))
|
||||
if (!(psxCounters[i].mode.gateEnable))
|
||||
return; // Ignore Gate
|
||||
|
||||
switch ((psxCounters[i].mode & 0x6) >> 1)
|
||||
switch (psxCounters[i].mode.gateMode)
|
||||
{
|
||||
case 0x0: // GATE_ON_count - count while gate signal is low (RENDER)
|
||||
|
||||
|
@ -327,7 +337,7 @@ static void _psxCheckStartGate(int i)
|
|||
psxRcntRcount32(i);
|
||||
|
||||
// Not strictly necessary.
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
psxCounters[i].startCycle = psxRegs.cycle & ~(psxCounters[i].rate - 1);
|
||||
break;
|
||||
|
||||
case 0x1: // GATE_ON_ClearStart - Counts constantly, clears on Blank END
|
||||
|
@ -335,10 +345,10 @@ static void _psxCheckStartGate(int i)
|
|||
break;
|
||||
|
||||
case 0x2: // GATE_ON_Clear_OFF_Start - Counts only when Blanking, clears on both ends, starts counting on next Blank Start.
|
||||
psxCounters[i].mode &= ~IOPCNT_STOPPED;
|
||||
psxRcntSync(i);
|
||||
psxCounters[i].mode.stopped = false;
|
||||
psxCounters[i].count = 0;
|
||||
psxCounters[i].target &= ~IOPCNT_FUTURE_TARGET;
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
break;
|
||||
|
||||
case 0x3: //GATE_ON_Start - Starts counting when the next Blank Ends, no clear.
|
||||
|
@ -349,33 +359,34 @@ static void _psxCheckStartGate(int i)
|
|||
|
||||
static void _psxCheckEndGate(int i)
|
||||
{
|
||||
if (!(psxCounters[i].mode & IOPCNT_ENABLE_GATE))
|
||||
if (!(psxCounters[i].mode.gateEnable))
|
||||
return; // Ignore Gate
|
||||
|
||||
// NOTE: Starting and stopping of modes 0 and 2 are checked in psxRcntCanCount(), only need to update the start cycle and counts.
|
||||
switch ((psxCounters[i].mode & 0x6) >> 1)
|
||||
switch (psxCounters[i].mode.gateMode)
|
||||
{
|
||||
case 0x0: // GATE_ON_count - count while gate signal is low (RENDER)
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
psxCounters[i].startCycle = psxRegs.cycle & ~(psxCounters[i].rate - 1);
|
||||
break;
|
||||
|
||||
case 0x1: // GATE_ON_ClearStart - Counts constantly, clears on Blank END
|
||||
psxRcntSync(i);
|
||||
psxCounters[i].count = 0;
|
||||
psxCounters[i].target &= ~IOPCNT_FUTURE_TARGET;
|
||||
break;
|
||||
|
||||
case 0x2: // GATE_ON_Clear_OFF_Start - Counts only when Blanking, clears on both ends, starts counting on next Blank Start.
|
||||
// No point in updating the count, since we're gonna clear it.
|
||||
psxRcntSync(i);
|
||||
psxCounters[i].count = 0;
|
||||
psxCounters[i].target &= ~IOPCNT_FUTURE_TARGET;
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
break; // do not set the counter
|
||||
|
||||
case 0x3: // GATE_ON_Start - Starts counting when the next Blank Ends, no clear.
|
||||
if (psxCounters[i].mode & IOPCNT_STOPPED)
|
||||
if (psxCounters[i].mode.stopped)
|
||||
{
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
psxCounters[i].mode &= ~IOPCNT_STOPPED;
|
||||
psxCounters[i].startCycle = psxRegs.cycle & ~(psxCounters[i].rate - 1);
|
||||
psxCounters[i].mode.stopped = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -446,8 +457,8 @@ void psxRcntUpdate()
|
|||
{
|
||||
int i;
|
||||
|
||||
psxNextCounter = 0x7fffffff;
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
psxNextDeltaCounter = 0x7fffffff;
|
||||
psxNextStartCounter = psxRegs.cycle;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
|
@ -455,58 +466,39 @@ void psxRcntUpdate()
|
|||
// We can't check the ALTSOURCE flag because the PSXCLOCK source *should*
|
||||
// be counted here.
|
||||
|
||||
if (!psxRcntCanCount(i))
|
||||
continue;
|
||||
|
||||
if ((psxCounters[i].mode & IOPCNT_INT_REPEAT) && !(psxCounters[i].mode & IOPCNT_INT_TOGGLE))
|
||||
{ //Repeat IRQ mode Pulsed, resets a few cycles after the interrupt, this should do.
|
||||
psxCounters[i].mode |= IOPCNT_INT_REQ;
|
||||
}
|
||||
psxRcntSync(i);
|
||||
|
||||
if (psxCounters[i].rate == PSXHBLANK)
|
||||
continue;
|
||||
|
||||
if (psxCounters[i].rate != 1)
|
||||
{
|
||||
const u32 change = (psxRegs.cycle - psxCounters[i].sCycleT) / psxCounters[i].rate;
|
||||
|
||||
if (change > 0)
|
||||
{
|
||||
psxCounters[i].count += change;
|
||||
psxCounters[i].sCycleT += change * psxCounters[i].rate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
psxCounters[i].count += psxRegs.cycle - psxCounters[i].sCycleT;
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
}
|
||||
if (!psxRcntCanCount(i))
|
||||
continue;
|
||||
|
||||
_rcntTestOverflow(i);
|
||||
_rcntTestTarget(i);
|
||||
}
|
||||
|
||||
const u32 spu2_delta = (psxRegs.cycle - lClocks) % 768;
|
||||
psxCounters[6].sCycleT = psxRegs.cycle;
|
||||
psxCounters[6].CycleT = psxCounters[6].rate - spu2_delta;
|
||||
psxCounters[6].startCycle = psxRegs.cycle - spu2_delta;
|
||||
psxCounters[6].deltaCycles = psxCounters[6].rate;
|
||||
SPU2async();
|
||||
psxNextCounter = psxCounters[6].CycleT;
|
||||
psxNextDeltaCounter = psxCounters[6].deltaCycles;
|
||||
|
||||
DEV9async(1);
|
||||
const s32 diffusb = psxRegs.cycle - psxCounters[7].sCycleT;
|
||||
s32 cusb = psxCounters[7].CycleT;
|
||||
DEV9async(1);
|
||||
const s32 diffusb = psxRegs.cycle - psxCounters[7].startCycle;
|
||||
s32 cusb = psxCounters[7].deltaCycles;
|
||||
|
||||
if (diffusb >= psxCounters[7].CycleT)
|
||||
if (diffusb >= psxCounters[7].deltaCycles)
|
||||
{
|
||||
USBasync(diffusb);
|
||||
psxCounters[7].sCycleT += psxCounters[7].rate * (diffusb / psxCounters[7].rate);
|
||||
psxCounters[7].CycleT = psxCounters[7].rate;
|
||||
psxCounters[7].startCycle += psxCounters[7].rate * (diffusb / psxCounters[7].rate);
|
||||
psxCounters[7].deltaCycles = psxCounters[7].rate;
|
||||
}
|
||||
else
|
||||
cusb -= diffusb;
|
||||
|
||||
if (cusb < psxNextCounter)
|
||||
psxNextCounter = cusb;
|
||||
if (cusb < psxNextDeltaCounter)
|
||||
psxNextDeltaCounter = cusb;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
_rcntSet(i);
|
||||
|
@ -517,15 +509,10 @@ void psxRcntUpdate()
|
|||
void psxRcntWcount16(int index, u16 value)
|
||||
{
|
||||
pxAssert(index < 3);
|
||||
//DevCon.Warning("16bit IOP Counter[%d] writeCount16 = %x", index, value);
|
||||
PSXCNT_LOG("16bit IOP Counter[%d] writeCount16 = %x", index, value);
|
||||
|
||||
psxRcntSync(index);
|
||||
|
||||
if (psxCounters[index].rate != PSXHBLANK)
|
||||
{
|
||||
const u32 change = (psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate;
|
||||
psxCounters[index].sCycleT += change * psxCounters[index].rate;
|
||||
}
|
||||
else
|
||||
psxCounters[index].sCycleT = psxRegs.cycle;
|
||||
psxCounters[index].count = value & 0xffff;
|
||||
|
||||
psxCounters[index].target &= 0xffff;
|
||||
|
@ -547,16 +534,8 @@ void psxRcntWcount32(int index, u32 value)
|
|||
pxAssert(index >= 3 && index < 6);
|
||||
PSXCNT_LOG("32bit IOP Counter[%d] writeCount32 = %x", index, value);
|
||||
|
||||
if (psxCounters[index].rate != PSXHBLANK)
|
||||
{
|
||||
// Re-adjust the sCycleT to match where the counter is currently
|
||||
// (remainder of the rate divided into the time passed will do the trick)
|
||||
psxRcntSync(index);
|
||||
|
||||
const u32 change = (psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate;
|
||||
psxCounters[index].sCycleT += change * psxCounters[index].rate;
|
||||
}
|
||||
else
|
||||
psxCounters[index].sCycleT = psxRegs.cycle;
|
||||
psxCounters[index].count = value;
|
||||
|
||||
psxCounters[index].target &= 0xffffffff;
|
||||
|
@ -580,10 +559,16 @@ __fi void psxRcntWmode16(int index, u32 value)
|
|||
|
||||
pxAssume(index >= 0 && index < 3);
|
||||
psxCounter& counter = psxCounters[index];
|
||||
psxCounterMode oldMode = counter.mode;
|
||||
|
||||
counter.mode = (value & IOPCNT_MODE_WRITE_MSK) | (counter.mode & IOPCNT_MODE_FLAG_MSK); // Write new value, preserve flags
|
||||
counter.mode |= IOPCNT_INT_REQ; // IRQ Enable
|
||||
counter.mode.modeval = (value & IOPCNT_MODE_WRITE_MSK) | (counter.mode.modeval & IOPCNT_MODE_FLAG_MSK); // Write new value, preserve flags
|
||||
|
||||
if (!((oldMode.targetFlag || oldMode.overflowFlag) && (oldMode.targetIntr || oldMode.overflIntr)))
|
||||
psxRcntSetNewIntrMode(index);
|
||||
|
||||
if (counter.mode.repeatIntr != counter.currentIrqMode.repeatInterrupt || counter.mode.toggleIntr != counter.currentIrqMode.toggleInterrupt)
|
||||
DevCon.Warning("Write to psxCounter[%d] mode old repeat %d new %d old toggle %d new %d", index, counter.currentIrqMode.repeatInterrupt, counter.mode.repeatIntr, counter.currentIrqMode.toggleInterrupt, counter.mode.toggleIntr);
|
||||
|
||||
if (value & (1 << 4))
|
||||
{
|
||||
irqmode += 1;
|
||||
|
@ -610,35 +595,27 @@ __fi void psxRcntWmode16(int index, u32 value)
|
|||
}
|
||||
if (index == 2)
|
||||
{
|
||||
switch (value & 0x200)
|
||||
{
|
||||
case 0x000:
|
||||
psxCounters[2].rate = 1;
|
||||
break;
|
||||
case 0x200:
|
||||
psxCounters[2].rate = 8;
|
||||
break;
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
if (counter.mode.t2Prescale)
|
||||
psxCounters[2].rate = 8;
|
||||
else
|
||||
psxCounters[2].rate = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counters 0 and 1 can select PIXEL or HSYNC as an alternate source:
|
||||
counter.rate = 1;
|
||||
|
||||
if (value & IOPCNT_ALT_SOURCE)
|
||||
if (counter.mode.extSignal)
|
||||
counter.rate = (index == 0) ? PSXPIXEL : PSXHBLANK;
|
||||
|
||||
if (counter.rate == PSXPIXEL)
|
||||
Console.Warning("PSX Pixel clock set to time 0, sync may be incorrect");
|
||||
|
||||
if (counter.mode & IOPCNT_ENABLE_GATE)
|
||||
if (counter.mode.gateEnable)
|
||||
{
|
||||
// If set to gate mode 3, the counting starts at the end of the next blank depending on which counter.
|
||||
if ((counter.mode & IOPCNT_MODE_GATE) == 0x4 && !psxRcntCanCount(index))
|
||||
counter.mode |= IOPCNT_STOPPED;
|
||||
else if ((counter.mode & IOPCNT_MODE_GATE) == 0x6)
|
||||
counter.mode |= IOPCNT_STOPPED;
|
||||
// If set to gate mode 2 or 3, the counting starts at the start and end of the next blank respectively depending on which counter.
|
||||
if (counter.mode.gateMode >= IOPCNT_GATE_CNT_HIGH_ZERO_OFF)
|
||||
counter.mode.stopped = true;
|
||||
|
||||
PSXCNT_LOG("IOP Counter[%d] Gate Check set, value = 0x%04X", index, value);
|
||||
}
|
||||
|
@ -646,8 +623,7 @@ __fi void psxRcntWmode16(int index, u32 value)
|
|||
|
||||
// Current counter *always* resets on mode write.
|
||||
counter.count = 0;
|
||||
counter.sCycleT = psxRegs.cycle;
|
||||
|
||||
counter.startCycle = psxRegs.cycle & ~(counter.rate - 1);
|
||||
counter.target &= 0xffff;
|
||||
|
||||
_rcntSet(index);
|
||||
|
@ -661,10 +637,16 @@ __fi void psxRcntWmode32(int index, u32 value)
|
|||
int irqmode = 0;
|
||||
pxAssume(index >= 3 && index < 6);
|
||||
psxCounter& counter = psxCounters[index];
|
||||
psxCounterMode oldMode = counter.mode;
|
||||
|
||||
counter.mode = (value & IOPCNT_MODE_WRITE_MSK) | (counter.mode & IOPCNT_MODE_FLAG_MSK); // Write new value, preserve flags
|
||||
counter.mode |= IOPCNT_INT_REQ; // IRQ Enable
|
||||
counter.mode.modeval = (value & IOPCNT_MODE_WRITE_MSK) | (counter.mode.modeval & IOPCNT_MODE_FLAG_MSK); // Write new value, preserve flags
|
||||
|
||||
if (!((oldMode.targetFlag || oldMode.overflowFlag) && (oldMode.targetIntr || oldMode.overflIntr)))
|
||||
psxRcntSetNewIntrMode(index);
|
||||
|
||||
if (counter.mode.repeatIntr != counter.currentIrqMode.repeatInterrupt || counter.mode.toggleIntr != counter.currentIrqMode.toggleInterrupt)
|
||||
DevCon.Warning("Write to psxCounter[%d] mode old repeat %d new %d old toggle %d new %d", index, counter.currentIrqMode.repeatInterrupt, counter.mode.repeatIntr, counter.currentIrqMode.toggleInterrupt, counter.mode.toggleIntr);
|
||||
|
||||
if (value & (1 << 4))
|
||||
{
|
||||
irqmode += 1;
|
||||
|
@ -693,39 +675,40 @@ __fi void psxRcntWmode32(int index, u32 value)
|
|||
{
|
||||
// Counter 3 has the HBlank as an alternate source.
|
||||
counter.rate = 1;
|
||||
if (value & IOPCNT_ALT_SOURCE)
|
||||
|
||||
if (counter.mode.extSignal)
|
||||
counter.rate = PSXHBLANK;
|
||||
|
||||
if (counter.mode & IOPCNT_ENABLE_GATE)
|
||||
if (counter.mode.gateEnable)
|
||||
{
|
||||
PSXCNT_LOG("IOP Counter[3] Gate Check set, value = %x", value);
|
||||
// If set to gate mode 2 or 3, the counting starts at the start and end of the next blank respectively depending on which counter.
|
||||
if ((counter.mode & IOPCNT_MODE_GATE) > 0x2)
|
||||
counter.mode |= IOPCNT_STOPPED;
|
||||
if (counter.mode.gateMode >= IOPCNT_GATE_CNT_HIGH_ZERO_OFF)
|
||||
counter.mode.stopped = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (value & 0x6000)
|
||||
switch (counter.mode.t4_5Prescale)
|
||||
{
|
||||
case 0x0000:
|
||||
counter.rate = 1;
|
||||
break;
|
||||
case 0x2000:
|
||||
case 0x1:
|
||||
counter.rate = 8;
|
||||
break;
|
||||
case 0x4000:
|
||||
case 0x2:
|
||||
counter.rate = 16;
|
||||
break;
|
||||
case 0x6000:
|
||||
case 0x3:
|
||||
counter.rate = 256;
|
||||
break;
|
||||
default:
|
||||
counter.rate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Current counter *always* resets on mode write.
|
||||
counter.count = 0;
|
||||
counter.sCycleT = psxRegs.cycle;
|
||||
counter.startCycle = psxRegs.cycle & ~(counter.rate - 1);
|
||||
counter.target &= 0xffffffff;
|
||||
_rcntSet(index);
|
||||
}
|
||||
|
@ -738,24 +721,7 @@ void psxRcntWtarget16(int index, u32 value)
|
|||
PSXCNT_LOG("IOP Counter[%d] writeTarget16 = %lx", index, value);
|
||||
psxCounters[index].target = value & 0xffff;
|
||||
|
||||
if (!(psxCounters[index].mode & IOPCNT_INT_TOGGLE))
|
||||
{
|
||||
// Pulse mode reset
|
||||
psxCounters[index].mode |= IOPCNT_INT_REQ; // Interrupt flag reset to high
|
||||
}
|
||||
|
||||
if (psxRcntCanCount(index) &&
|
||||
(psxCounters[index].rate != PSXHBLANK))
|
||||
{
|
||||
// Re-adjust the sCycleT to match where the counter is currently
|
||||
// (remainder of the rate divided into the time passed will do the trick)
|
||||
|
||||
const u32 change = (psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate;
|
||||
psxCounters[index].count += change;
|
||||
psxCounters[index].sCycleT += change * psxCounters[index].rate;
|
||||
}
|
||||
else
|
||||
psxCounters[index].sCycleT = psxRegs.cycle;
|
||||
psxRcntSync(index);
|
||||
|
||||
// protect the target from an early arrival.
|
||||
// if the target is behind the current count, then set the target overflow
|
||||
|
@ -770,28 +736,11 @@ void psxRcntWtarget16(int index, u32 value)
|
|||
void psxRcntWtarget32(int index, u32 value)
|
||||
{
|
||||
pxAssert(index >= 3 && index < 6);
|
||||
PSXCNT_LOG("IOP Counter[%d] writeTarget32 = %lx mode %x", index, value, psxCounters[index].mode);
|
||||
PSXCNT_LOG("IOP Counter[%d] writeTarget32 = %lx mode %x", index, value, psxCounters[index].mode.modeval);
|
||||
|
||||
psxCounters[index].target = value;
|
||||
|
||||
if (!(psxCounters[index].mode & IOPCNT_INT_TOGGLE))
|
||||
{
|
||||
// Pulse mode reset
|
||||
psxCounters[index].mode |= IOPCNT_INT_REQ; // Interrupt flag reset to high
|
||||
}
|
||||
|
||||
if (psxRcntCanCount(index) &&
|
||||
(psxCounters[index].rate != PSXHBLANK))
|
||||
{
|
||||
// Re-adjust the sCycleT to match where the counter is currently
|
||||
// (remainder of the rate divided into the time passed will do the trick)
|
||||
|
||||
const u32 change = (psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate;
|
||||
psxCounters[index].count += change;
|
||||
psxCounters[index].sCycleT += change * psxCounters[index].rate;
|
||||
}
|
||||
else
|
||||
psxCounters[index].sCycleT = psxRegs.cycle;
|
||||
psxRcntSync(index);
|
||||
// protect the target from an early arrival.
|
||||
// if the target is behind the current count, then set the target overflow
|
||||
// flag, so that the target won't be active until after the next overflow.
|
||||
|
@ -804,56 +753,50 @@ void psxRcntWtarget32(int index, u32 value)
|
|||
|
||||
u16 psxRcntRcount16(int index)
|
||||
{
|
||||
u32 retval = (u32)psxCounters[index].count;
|
||||
psxRcntSync(index);
|
||||
const u32 retval = (u32)psxCounters[index].count;
|
||||
|
||||
pxAssert(index < 3);
|
||||
|
||||
PSXCNT_LOG("IOP Counter[%d] readCount16 = %lx", index, (u16)retval);
|
||||
|
||||
// Don't count HBLANK timers
|
||||
// Don't count stopped gates either.
|
||||
const bool canCount = psxRcntCanCount(index);
|
||||
if ((psxCounters[index].rate != PSXHBLANK) && canCount)
|
||||
{
|
||||
u32 delta = (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate);
|
||||
retval += delta;
|
||||
PSXCNT_LOG(" (delta = %lx)", delta);
|
||||
}
|
||||
else if (!canCount && (psxCounters[index].mode & IOPCNT_ENABLE_GATE) && (psxCounters[index].mode & IOPCNT_MODE_GATE) == 4)
|
||||
retval = 0;
|
||||
|
||||
return (u16)retval;
|
||||
}
|
||||
|
||||
u32 psxRcntRcount32(int index)
|
||||
{
|
||||
u32 retval = (u32)psxCounters[index].count;
|
||||
psxRcntSync(index);
|
||||
|
||||
const u32 retval = (u32)psxCounters[index].count;
|
||||
|
||||
pxAssert(index >= 3 && index < 6);
|
||||
|
||||
PSXCNT_LOG("IOP Counter[%d] readCount32 = %lx", index, retval);
|
||||
|
||||
const bool canCount = psxRcntCanCount(index);
|
||||
if ((psxCounters[index].rate != PSXHBLANK) && canCount)
|
||||
{
|
||||
u32 delta = (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate);
|
||||
retval += delta;
|
||||
PSXCNT_LOG(" (delta = %lx)", delta);
|
||||
}
|
||||
else if (!canCount && (psxCounters[index].mode & IOPCNT_ENABLE_GATE) && (psxCounters[index].mode & IOPCNT_MODE_GATE) == 4)
|
||||
retval = 0;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void psxRcntSetNewIntrMode(int index)
|
||||
{
|
||||
psxCounters[index].mode.targetFlag = false;
|
||||
psxCounters[index].mode.overflowFlag = false;
|
||||
psxCounters[index].mode.intrEnable = true;
|
||||
|
||||
if (psxCounters[index].mode.repeatIntr != psxCounters[index].currentIrqMode.repeatInterrupt || psxCounters[index].mode.toggleIntr != psxCounters[index].currentIrqMode.toggleInterrupt)
|
||||
DevCon.Warning("Updating psxCounter[%d] mode old repeat %d new %d old toggle %d new %d", index, psxCounters[index].mode.repeatIntr, psxCounters[index].currentIrqMode.repeatInterrupt, psxCounters[index].mode.toggleIntr, psxCounters[index].currentIrqMode.toggleInterrupt);
|
||||
|
||||
psxCounters[index].currentIrqMode.repeatInterrupt = psxCounters[index].mode.repeatIntr;
|
||||
psxCounters[index].currentIrqMode.toggleInterrupt = psxCounters[index].mode.toggleIntr;
|
||||
}
|
||||
|
||||
bool SaveStateBase::psxRcntFreeze()
|
||||
{
|
||||
if (!FreezeTag("iopCounters"))
|
||||
return false;
|
||||
|
||||
Freeze(psxCounters);
|
||||
Freeze(psxNextCounter);
|
||||
Freeze(psxNextsCounter);
|
||||
Freeze(psxNextDeltaCounter);
|
||||
Freeze(psxNextStartCounter);
|
||||
Freeze(hBlanking);
|
||||
Freeze(vBlanking);
|
||||
|
||||
|
|
|
@ -5,12 +5,44 @@
|
|||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
|
||||
struct CounterIRQBehaviour
|
||||
{
|
||||
bool repeatInterrupt;
|
||||
bool toggleInterrupt;
|
||||
};
|
||||
|
||||
union psxCounterMode
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 gateEnable : 1;
|
||||
u32 gateMode : 2;
|
||||
u32 zeroReturn : 1;
|
||||
u32 targetIntr : 1;
|
||||
u32 overflIntr : 1;
|
||||
u32 repeatIntr : 1;
|
||||
u32 toggleIntr : 1;
|
||||
u32 extSignal : 1;
|
||||
u32 t2Prescale : 1;
|
||||
u32 intrEnable : 1;
|
||||
u32 targetFlag : 1;
|
||||
u32 overflowFlag : 1;
|
||||
u32 t4_5Prescale : 2;
|
||||
u32 stopped : 1;
|
||||
};
|
||||
|
||||
u32 modeval;
|
||||
};
|
||||
|
||||
struct psxCounter {
|
||||
u64 count, target;
|
||||
u32 mode;
|
||||
|
||||
u32 rate, interrupt;
|
||||
u32 sCycleT;
|
||||
s32 CycleT;
|
||||
u32 startCycle;
|
||||
s32 deltaCycles;
|
||||
|
||||
psxCounterMode mode;
|
||||
CounterIRQBehaviour currentIrqMode;
|
||||
};
|
||||
|
||||
#define NUM_COUNTERS 8
|
||||
|
@ -25,6 +57,7 @@ extern void psxRcntWmode16(int index, u32 value);
|
|||
extern void psxRcntWmode32(int index, u32 value);
|
||||
extern void psxRcntWtarget16(int index, u32 value);
|
||||
extern void psxRcntWtarget32(int index, u32 value);
|
||||
extern void psxRcntSetNewIntrMode(int index);
|
||||
extern u16 psxRcntRcount16(int index);
|
||||
extern u32 psxRcntRcount32(int index);
|
||||
extern u64 psxRcntCycles(int index);
|
||||
|
|
|
@ -32,20 +32,20 @@ static void psxDmaGeneric(u32 madr, u32 bcr, u32 chcr, u32 spuCore)
|
|||
// Update the spu2 to the current cycle before initiating the DMA
|
||||
|
||||
SPU2async();
|
||||
//Console.Status("cycles sent to SPU2 %x\n", psxRegs.cycle - psxCounters[6].sCycleT);
|
||||
//Console.Status("cycles sent to SPU2 %x\n", psxRegs.cycle - psxCounters[6].startCycle);
|
||||
|
||||
psxCounters[6].sCycleT = psxRegs.cycle;
|
||||
psxCounters[6].CycleT = size * 4;
|
||||
psxCounters[6].startCycle = psxRegs.cycle;
|
||||
psxCounters[6].deltaCycles = size * 4;
|
||||
|
||||
psxNextCounter -= (psxRegs.cycle - psxNextsCounter);
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].CycleT < psxNextCounter)
|
||||
psxNextCounter = psxCounters[6].CycleT;
|
||||
psxNextDeltaCounter -= (psxRegs.cycle - psxNextStartCounter);
|
||||
psxNextStartCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].deltaCycles < psxNextDeltaCounter)
|
||||
psxNextDeltaCounter = psxCounters[6].deltaCycles;
|
||||
|
||||
if ((psxRegs.iopNextEventCycle - psxNextsCounter) > (u32)psxNextCounter)
|
||||
if ((psxRegs.iopNextEventCycle - psxNextStartCounter) > (u32)psxNextDeltaCounter)
|
||||
{
|
||||
//DevCon.Warning("SPU2async Setting new counter branch, old %x new %x ((%x - %x = %x) > %x delta)", g_iopNextEventCycle, psxNextsCounter + psxNextCounter, g_iopNextEventCycle, psxNextsCounter, (g_iopNextEventCycle - psxNextsCounter), psxNextCounter);
|
||||
psxRegs.iopNextEventCycle = psxNextsCounter + psxNextCounter;
|
||||
//DevCon.Warning("SPU2async Setting new counter branch, old %x new %x ((%x - %x = %x) > %x delta)", g_iopNextEventCycle, psxNextStartCounter + psxNextCounter, g_iopNextEventCycle, psxNextStartCounter, (g_iopNextEventCycle - psxNextStartCounter), psxNextCounter);
|
||||
psxRegs.iopNextEventCycle = psxNextStartCounter + psxNextDeltaCounter;
|
||||
}
|
||||
|
||||
switch (chcr)
|
||||
|
|
|
@ -207,7 +207,7 @@ __ri void iopEventTest()
|
|||
{
|
||||
psxRegs.iopNextEventCycle = psxRegs.cycle + iopWaitCycles;
|
||||
|
||||
if (psxTestCycle(psxNextsCounter, psxNextCounter))
|
||||
if (psxTestCycle(psxNextStartCounter, psxNextDeltaCounter))
|
||||
{
|
||||
psxRcntUpdate();
|
||||
iopEventAction = true;
|
||||
|
@ -216,8 +216,8 @@ __ri void iopEventTest()
|
|||
{
|
||||
// start the next branch at the next counter event by default
|
||||
// the interrupt code below will assign nearer branches if needed.
|
||||
if (psxNextCounter < static_cast<s32>(psxRegs.iopNextEventCycle - psxNextsCounter))
|
||||
psxRegs.iopNextEventCycle = psxNextsCounter + psxNextCounter;
|
||||
if (psxNextDeltaCounter < static_cast<s32>(psxRegs.iopNextEventCycle - psxNextStartCounter))
|
||||
psxRegs.iopNextEventCycle = psxNextStartCounter + psxNextDeltaCounter;
|
||||
}
|
||||
|
||||
if (psxRegs.interrupt)
|
||||
|
|
|
@ -168,8 +168,8 @@ extern u32 EEoCycle;
|
|||
|
||||
#endif
|
||||
|
||||
extern s32 psxNextCounter;
|
||||
extern u32 psxNextsCounter;
|
||||
extern s32 psxNextDeltaCounter;
|
||||
extern u32 psxNextStartCounter;
|
||||
extern bool iopEventAction;
|
||||
extern bool iopEventTestIsActive;
|
||||
|
||||
|
|
|
@ -398,7 +398,7 @@ __fi void _cpuEventTest_Shared()
|
|||
|
||||
iopEventTest();
|
||||
|
||||
if (cpuTestCycle(nextsCounter, nextCounter))
|
||||
if (cpuTestCycle(nextStartCounter, nextDeltaCounter))
|
||||
{
|
||||
rcntUpdate();
|
||||
_cpuTestPERF();
|
||||
|
@ -450,7 +450,7 @@ __fi void _cpuEventTest_Shared()
|
|||
}
|
||||
|
||||
// Apply vsync and other counter nextCycles
|
||||
cpuSetNextEvent(nextsCounter, nextCounter);
|
||||
cpuSetNextEvent(nextStartCounter, nextDeltaCounter);
|
||||
|
||||
eeEventTestIsActive = false;
|
||||
}
|
||||
|
|
|
@ -345,15 +345,15 @@ void V_Core::FinishDMAwrite()
|
|||
|
||||
DMAICounter = (DMAICounter - ReadSize) * 4;
|
||||
|
||||
if (((psxCounters[6].sCycleT + psxCounters[6].CycleT) - psxRegs.cycle) > (u32)DMAICounter)
|
||||
if (((psxCounters[6].startCycle + psxCounters[6].deltaCycles) - psxRegs.cycle) > (u32)DMAICounter)
|
||||
{
|
||||
psxCounters[6].sCycleT = psxRegs.cycle;
|
||||
psxCounters[6].CycleT = DMAICounter;
|
||||
psxCounters[6].startCycle = psxRegs.cycle;
|
||||
psxCounters[6].deltaCycles = DMAICounter;
|
||||
|
||||
psxNextCounter -= (psxRegs.cycle - psxNextsCounter);
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].CycleT < psxNextCounter)
|
||||
psxNextCounter = psxCounters[6].CycleT;
|
||||
psxNextDeltaCounter -= (psxRegs.cycle - psxNextStartCounter);
|
||||
psxNextStartCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].deltaCycles < psxNextDeltaCounter)
|
||||
psxNextDeltaCounter = psxCounters[6].deltaCycles;
|
||||
}
|
||||
|
||||
ActiveTSA = TDA;
|
||||
|
@ -439,15 +439,15 @@ void V_Core::FinishDMAread()
|
|||
else
|
||||
DMAICounter = 4;
|
||||
|
||||
if (((psxCounters[6].sCycleT + psxCounters[6].CycleT) - psxRegs.cycle) > (u32)DMAICounter)
|
||||
if (((psxCounters[6].startCycle + psxCounters[6].deltaCycles) - psxRegs.cycle) > (u32)DMAICounter)
|
||||
{
|
||||
psxCounters[6].sCycleT = psxRegs.cycle;
|
||||
psxCounters[6].CycleT = DMAICounter;
|
||||
psxCounters[6].startCycle = psxRegs.cycle;
|
||||
psxCounters[6].deltaCycles = DMAICounter;
|
||||
|
||||
psxNextCounter -= (psxRegs.cycle - psxNextsCounter);
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].CycleT < psxNextCounter)
|
||||
psxNextCounter = psxCounters[6].CycleT;
|
||||
psxNextDeltaCounter -= (psxRegs.cycle - psxNextStartCounter);
|
||||
psxNextStartCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].deltaCycles < psxNextDeltaCounter)
|
||||
psxNextDeltaCounter = psxCounters[6].deltaCycles;
|
||||
}
|
||||
|
||||
ActiveTSA = TDA;
|
||||
|
@ -470,15 +470,15 @@ void V_Core::DoDMAread(u16* pMem, u32 size)
|
|||
//Regs.ATTR |= 0x30;
|
||||
TADR = MADR + (size << 1);
|
||||
|
||||
if (((psxCounters[6].sCycleT + psxCounters[6].CycleT) - psxRegs.cycle) > (u32)DMAICounter)
|
||||
if (((psxCounters[6].startCycle + psxCounters[6].deltaCycles) - psxRegs.cycle) > (u32)DMAICounter)
|
||||
{
|
||||
psxCounters[6].sCycleT = psxRegs.cycle;
|
||||
psxCounters[6].CycleT = DMAICounter;
|
||||
psxCounters[6].startCycle = psxRegs.cycle;
|
||||
psxCounters[6].deltaCycles = DMAICounter;
|
||||
|
||||
psxNextCounter -= (psxRegs.cycle - psxNextsCounter);
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].CycleT < psxNextCounter)
|
||||
psxNextCounter = psxCounters[6].CycleT;
|
||||
psxNextDeltaCounter -= (psxRegs.cycle - psxNextStartCounter);
|
||||
psxNextStartCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].deltaCycles < psxNextDeltaCounter)
|
||||
psxNextDeltaCounter = psxCounters[6].deltaCycles;
|
||||
}
|
||||
|
||||
if (SPU2::MsgDMA())
|
||||
|
|
|
@ -349,15 +349,15 @@ __forceinline void TimeUpdate(u32 cClocks)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (((psxCounters[6].sCycleT + psxCounters[6].CycleT) - psxRegs.cycle) > (u32)Cores[0].DMAICounter)
|
||||
if (((psxCounters[6].startCycle + psxCounters[6].deltaCycles) - psxRegs.cycle) > (u32)Cores[0].DMAICounter)
|
||||
{
|
||||
psxCounters[6].sCycleT = psxRegs.cycle;
|
||||
psxCounters[6].CycleT = Cores[0].DMAICounter;
|
||||
psxCounters[6].startCycle = psxRegs.cycle;
|
||||
psxCounters[6].deltaCycles = Cores[0].DMAICounter;
|
||||
|
||||
psxNextCounter -= (psxRegs.cycle - psxNextsCounter);
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].CycleT < psxNextCounter)
|
||||
psxNextCounter = psxCounters[6].CycleT;
|
||||
psxNextDeltaCounter -= (psxRegs.cycle - psxNextStartCounter);
|
||||
psxNextStartCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].deltaCycles < psxNextDeltaCounter)
|
||||
psxNextDeltaCounter = psxCounters[6].deltaCycles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -402,15 +402,15 @@ __forceinline void TimeUpdate(u32 cClocks)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (((psxCounters[6].sCycleT + psxCounters[6].CycleT) - psxRegs.cycle) > (u32)Cores[1].DMAICounter)
|
||||
if (((psxCounters[6].startCycle + psxCounters[6].deltaCycles) - psxRegs.cycle) > (u32)Cores[1].DMAICounter)
|
||||
{
|
||||
psxCounters[6].sCycleT = psxRegs.cycle;
|
||||
psxCounters[6].CycleT = Cores[1].DMAICounter;
|
||||
psxCounters[6].startCycle = psxRegs.cycle;
|
||||
psxCounters[6].deltaCycles = Cores[1].DMAICounter;
|
||||
|
||||
psxNextCounter -= (psxRegs.cycle - psxNextsCounter);
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].CycleT < psxNextCounter)
|
||||
psxNextCounter = psxCounters[6].CycleT;
|
||||
psxNextDeltaCounter -= (psxRegs.cycle - psxNextStartCounter);
|
||||
psxNextStartCounter = psxRegs.cycle;
|
||||
if (psxCounters[6].deltaCycles < psxNextDeltaCounter)
|
||||
psxNextDeltaCounter = psxCounters[6].deltaCycles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,10 +192,10 @@ bool SaveStateBase::FreezeInternals(Error* error)
|
|||
|
||||
Freeze(EEsCycle);
|
||||
Freeze(EEoCycle);
|
||||
Freeze(nextCounter);
|
||||
Freeze(nextsCounter);
|
||||
Freeze(psxNextsCounter);
|
||||
Freeze(psxNextCounter);
|
||||
Freeze(nextDeltaCounter);
|
||||
Freeze(nextStartCounter);
|
||||
Freeze(psxNextStartCounter);
|
||||
Freeze(psxNextDeltaCounter);
|
||||
|
||||
// Fourth Block - EE-related systems
|
||||
// ---------------------------------
|
||||
|
|
|
@ -25,7 +25,7 @@ enum class FreezeAction
|
|||
// [SAVEVERSION+]
|
||||
// This informs the auto updater that the users savestates will be invalidated.
|
||||
|
||||
static const u32 g_SaveVersion = (0x9A4D << 16) | 0x0000;
|
||||
static const u32 g_SaveVersion = (0x9A4E << 16) | 0x0000;
|
||||
|
||||
|
||||
// the freezing data between submodules and core
|
||||
|
|
|
@ -161,13 +161,9 @@ static __fi T _HwRead_16or32_Page1( u32 addr )
|
|||
break;
|
||||
|
||||
case 0x4:
|
||||
ret = psxCounters[cntidx].mode;
|
||||
ret = psxCounters[cntidx].mode.modeval;
|
||||
|
||||
// hmm! The old code only did this bitwise math for 16 bit reads.
|
||||
// Logic indicates it should do the math consistently. Question is,
|
||||
// should it do the logic for both 16 and 32, or not do logic at all?
|
||||
|
||||
psxCounters[cntidx].mode &= ~0x1800;
|
||||
psxRcntSetNewIntrMode(cntidx);
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
|
@ -197,13 +193,10 @@ static __fi T _HwRead_16or32_Page1( u32 addr )
|
|||
break;
|
||||
|
||||
case 0x4:
|
||||
ret = psxCounters[cntidx].mode;
|
||||
ret = psxCounters[cntidx].mode.modeval;
|
||||
PSXCNT_LOG("IOP Counter[%d] modeRead%d = %lx", cntidx, sizeof(T) * 8, ret);
|
||||
// hmm! The old code only did the following bitwise math for 16 bit reads.
|
||||
// Logic indicates it should do the math consistently. Question is,
|
||||
// should it do the logic for both 16 and 32, or not do logic at all?
|
||||
|
||||
psxCounters[cntidx].mode &= ~0x1800;
|
||||
psxRcntSetNewIntrMode(cntidx);
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
|
|
Loading…
Reference in New Issue