mirror of https://github.com/PCSX2/pcsx2.git
EE/IOP Timers: Rewrote most of the gate handling to be better.
[SAVEVERSION+]
This commit is contained in:
parent
4363255234
commit
5f7c2b7cd8
|
@ -21,9 +21,7 @@
|
|||
#include "VMManager.h"
|
||||
#include "VUmicro.h"
|
||||
|
||||
extern u8 psxhblankgate;
|
||||
static const uint EECNT_FUTURE_TARGET = 0x10000000;
|
||||
static int gates = 0;
|
||||
|
||||
uint g_FrameCount = 0;
|
||||
|
||||
|
@ -75,7 +73,7 @@ static __fi void _rcntSet(int cntidx)
|
|||
const Counter& counter = counters[cntidx];
|
||||
|
||||
// Stopped or special hsync gate?
|
||||
if (!counter.mode.IsCounting || (counter.mode.ClockSource == 0x3))
|
||||
if (!rcntCanCount(cntidx) || (counter.mode.ClockSource == 0x3))
|
||||
return;
|
||||
|
||||
if (!counter.mode.TargetInterrupt && !counter.mode.OverflowInterrupt)
|
||||
|
@ -232,7 +230,7 @@ static void vSyncInfoCalc(vSyncTimingInfo* info, double framesPerSecond, u32 sca
|
|||
{
|
||||
hBlank /= 2;
|
||||
hRender /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Carry fixed-point math all the way through the entire vsync and hsync counting processes, and continually apply rounding
|
||||
//as needed for each scheduled v/hsync related event. Much better to handle than this messed state.
|
||||
|
@ -512,8 +510,7 @@ static __fi void VSyncStart(u32 sCycle)
|
|||
// Memcard IO detection - Uses a tick system to determine when memcards are no longer being written.
|
||||
MemcardBusy::Decrement();
|
||||
|
||||
if (gates)
|
||||
rcntStartGate(true, sCycle); // Counters Start Gate code
|
||||
rcntStartGate(true, sCycle); // Counters Start Gate code
|
||||
|
||||
// INTC - VB Blank Start Hack --
|
||||
// Hack fix! This corrects a freezeup in Granda 2 where it decides to spin
|
||||
|
@ -570,8 +567,7 @@ static __fi void VSyncEnd(u32 sCycle)
|
|||
|
||||
hwIntcIrq(INTC_VBLANK_E); // HW Irq
|
||||
psxVBlankEnd(); // psxCounters vBlank End
|
||||
if (gates)
|
||||
rcntEndGate(true, sCycle); // Counters End Gate Code
|
||||
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
|
||||
|
@ -597,13 +593,14 @@ __fi void rcntUpdate_hScanline()
|
|||
//iopEventAction = 1;
|
||||
if (hsyncCounter.Mode == MODE_HBLANK)
|
||||
{ //HBLANK Start
|
||||
|
||||
rcntStartGate(false, hsyncCounter.sCycle + hsyncCounter.CycleT);
|
||||
psxHBlankStart();
|
||||
|
||||
// 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;
|
||||
|
||||
rcntStartGate(false, hsyncCounter.sCycle);
|
||||
psxCheckStartGate16(0);
|
||||
}
|
||||
else
|
||||
{ //HBLANK END / HRENDER Begin
|
||||
|
@ -614,16 +611,15 @@ __fi void rcntUpdate_hScanline()
|
|||
gsIrq();
|
||||
}
|
||||
|
||||
rcntEndGate(false, hsyncCounter.sCycle + hsyncCounter.CycleT);
|
||||
|
||||
psxHBlankEnd();
|
||||
|
||||
// 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;
|
||||
|
||||
if (gates)
|
||||
rcntEndGate(false, hsyncCounter.sCycle);
|
||||
if (psxhblankgate)
|
||||
psxCheckEndGate16(0);
|
||||
|
||||
#ifdef VSYNC_DEBUG
|
||||
hsc++;
|
||||
#endif
|
||||
|
@ -637,11 +633,11 @@ __fi void rcntUpdate_vSync()
|
|||
|
||||
if (vsyncCounter.Mode == MODE_VSYNC)
|
||||
{
|
||||
VSyncEnd(vsyncCounter.sCycle + vsyncCounter.CycleT);
|
||||
|
||||
vsyncCounter.sCycle += vSyncInfo.Blank;
|
||||
vsyncCounter.CycleT = vSyncInfo.Render;
|
||||
vsyncCounter.Mode = MODE_VRENDER;
|
||||
|
||||
VSyncEnd(vsyncCounter.sCycle);
|
||||
vsyncCounter.Mode = MODE_VRENDER; // VSYNC END - Render begin
|
||||
}
|
||||
else if (vsyncCounter.Mode == MODE_GSBLANK) // GS CSR Swap and interrupt
|
||||
{
|
||||
|
@ -651,8 +647,10 @@ __fi void rcntUpdate_vSync()
|
|||
// Don't set the start cycle, makes it easier to calculate the correct Vsync End time
|
||||
vsyncCounter.CycleT = vSyncInfo.Blank;
|
||||
}
|
||||
else // VSYNC end / VRENDER begin
|
||||
else // VSYNC Start
|
||||
{
|
||||
VSyncStart(vsyncCounter.sCycle + vsyncCounter.CycleT);
|
||||
|
||||
vsyncCounter.sCycle += vSyncInfo.Render;
|
||||
vsyncCounter.CycleT = vSyncInfo.GSBlank;
|
||||
vsyncCounter.Mode = MODE_GSBLANK;
|
||||
|
@ -660,8 +658,6 @@ __fi void rcntUpdate_vSync()
|
|||
// Accumulate hsync rounding errors:
|
||||
hsyncCounter.sCycle += vSyncInfo.hSyncError;
|
||||
|
||||
VSyncStart(vsyncCounter.sCycle);
|
||||
|
||||
#ifdef VSYNC_DEBUG
|
||||
vblankinc++;
|
||||
if (vblankinc > 1)
|
||||
|
@ -717,7 +713,20 @@ static __fi void _cpuTestOverflow(int i)
|
|||
}
|
||||
|
||||
|
||||
// forceinline note: this method is called from two locations, but one
|
||||
__fi bool rcntCanCount(int i)
|
||||
{
|
||||
if (!counters[i].mode.IsCounting)
|
||||
return false;
|
||||
|
||||
if (!counters[i].mode.EnableGate)
|
||||
return true;
|
||||
|
||||
// If we're in gate mode, we can only count if it's not both gated and counting on HBLANK or GateMode is not 0 (Count only when low) or the signal is low.
|
||||
return ((counters[i].mode.GateSource == 0 && counters[i].mode.ClockSource != 3 && (hsyncCounter.Mode == MODE_HRENDER || counters[i].mode.GateMode != 0)) ||
|
||||
(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
|
||||
// of them is the interpreter, which doesn't count. ;) So might as
|
||||
// well forceinline it!
|
||||
__fi void rcntUpdate()
|
||||
|
@ -730,12 +739,7 @@ __fi void rcntUpdate()
|
|||
|
||||
for (int i = 0; i <= 3; i++)
|
||||
{
|
||||
// We want to count gated counters (except the hblank which exclude below, and are
|
||||
// counted by the hblank timer instead)
|
||||
|
||||
//if ( gates & (1<<i) ) continue;
|
||||
|
||||
if (!counters[i].mode.IsCounting)
|
||||
if (!rcntCanCount(i))
|
||||
continue;
|
||||
|
||||
if (counters[i].mode.ClockSource != 0x3) // don't count hblank sources
|
||||
|
@ -761,34 +765,21 @@ static __fi void _rcntSetGate(int index)
|
|||
if (counters[index].mode.EnableGate)
|
||||
{
|
||||
// If the Gate Source is hblank and the clock selection is also hblank
|
||||
// then the gate is disabled and the counter acts as a normal hblank source.
|
||||
|
||||
// the timer completely turns off (HW Tested).
|
||||
if (!(counters[index].mode.GateSource == 0 && counters[index].mode.ClockSource == 3))
|
||||
{
|
||||
EECNT_LOG("EE Counter[%d] Using Gate! Source=%s, Mode=%d.",
|
||||
index, counters[index].mode.GateSource ? "vblank" : "hblank", counters[index].mode.GateMode);
|
||||
|
||||
gates |= (1 << index);
|
||||
// FIXME: Test required - should the counter be stopped here? I feel like it should only stop and reset on the gate signal happening.
|
||||
counters[index].mode.IsCounting = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
EECNT_LOG("EE Counter[%d] GATE DISABLED because of hblank source.", index);
|
||||
}
|
||||
|
||||
gates &= ~(1 << index);
|
||||
}
|
||||
|
||||
// mode - 0 means hblank source, 8 means vblank source.
|
||||
static __fi void rcntStartGate(bool isVblank, u32 sCycle)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= 3; i++)
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
//if ((mode == 0) && ((counters[i].mode & 0x83) == 0x83))
|
||||
if (!isVblank && counters[i].mode.IsCounting && (counters[i].mode.ClockSource == 3))
|
||||
if (!isVblank && (counters[i].mode.ClockSource == 3) && rcntCanCount(i))
|
||||
{
|
||||
// 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
|
||||
|
@ -802,35 +793,31 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
|
|||
_cpuTestTarget(i);
|
||||
}
|
||||
|
||||
if (!(gates & (1 << i)))
|
||||
if (!counters[i].mode.EnableGate)
|
||||
continue;
|
||||
|
||||
if ((!!counters[i].mode.GateSource) != isVblank)
|
||||
continue;
|
||||
|
||||
switch (counters[i].mode.GateMode)
|
||||
{
|
||||
case 0x0: //Count When Signal is low (off)
|
||||
case 0x0: //Count When Signal is low (V_RENDER ONLY)
|
||||
|
||||
// Just set the start cycle (sCycleT) -- counting will be done as needed
|
||||
// for events (overflows, targets, mode changes, and the gate off below)
|
||||
|
||||
counters[i].count = rcntRcount(i);
|
||||
counters[i].mode.IsCounting = 0;
|
||||
counters[i].sCycleT = sCycle;
|
||||
EECNT_LOG("EE Counter[%d] %s StartGate Type0, count = %x", i,
|
||||
isVblank ? "vblank" : "hblank", counters[i].count);
|
||||
break;
|
||||
|
||||
case 0x2: // reset and start counting on vsync end
|
||||
// this is the vsync start so do nothing.
|
||||
case 0x2: // Reset on Vsync end
|
||||
// This is the vsync start so do nothing.
|
||||
break;
|
||||
|
||||
case 0x1: //Reset and start counting on Vsync start
|
||||
case 0x3: //Reset and start counting on Vsync start and end
|
||||
counters[i].mode.IsCounting = 1;
|
||||
case 0x1: // Reset on Vsync start
|
||||
case 0x3: // Reset on Vsync start and end
|
||||
counters[i].count = 0;
|
||||
counters[i].target &= 0xffff;
|
||||
counters[i].sCycleT = sCycle;
|
||||
EECNT_LOG("EE Counter[%d] %s StartGate Type%d, count = %x", i,
|
||||
isVblank ? "vblank" : "hblank", counters[i].mode.GateMode, counters[i].count);
|
||||
break;
|
||||
|
@ -848,41 +835,33 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
|
|||
// mode - 0 means hblank signal, 8 means vblank signal.
|
||||
static __fi void rcntEndGate(bool isVblank, u32 sCycle)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= 3; i++)
|
||||
{ //Gates for counters
|
||||
if (!(gates & (1 << i)))
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (!counters[i].mode.EnableGate)
|
||||
continue;
|
||||
|
||||
if ((!!counters[i].mode.GateSource) != isVblank)
|
||||
continue;
|
||||
|
||||
switch (counters[i].mode.GateMode)
|
||||
{
|
||||
case 0x0: //Count When Signal is low (off)
|
||||
|
||||
// Set the count here. Since the timer is being turned off it's
|
||||
// important to record its count at this point (it won't be counted by
|
||||
// calls to rcntUpdate).
|
||||
counters[i].mode.IsCounting = 1;
|
||||
counters[i].sCycleT = cpuRegs.cycle;
|
||||
case 0x0: //Count When Signal is low (V_RENDER ONLY)
|
||||
counters[i].sCycleT = sCycle;
|
||||
|
||||
EECNT_LOG("EE Counter[%d] %s EndGate Type0, count = %x", i,
|
||||
isVblank ? "vblank" : "hblank", counters[i].count);
|
||||
break;
|
||||
|
||||
case 0x1: // Reset and start counting on Vsync start
|
||||
// this is the vsync end so do nothing
|
||||
case 0x1: // Reset on Vsync start
|
||||
// This is the vsync end so do nothing
|
||||
break;
|
||||
|
||||
case 0x2: //Reset and start counting on Vsync end
|
||||
case 0x3: //Reset and start counting on Vsync start and end
|
||||
counters[i].mode.IsCounting = 1;
|
||||
case 0x2: // Reset on Vsync end
|
||||
case 0x3: // Reset on Vsync start and end
|
||||
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;
|
||||
counters[i].sCycleT = sCycle;
|
||||
EECNT_LOG("EE Counter[%d] %s EndGate Type%d, count = %x", i,
|
||||
isVblank ? "vblank" : "hblank", counters[i].mode.GateMode, counters[i].count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -892,7 +871,7 @@ static __fi void rcntEndGate(bool isVblank, u32 sCycle)
|
|||
|
||||
static __fi void rcntWmode(int index, u32 value)
|
||||
{
|
||||
if (counters[index].mode.IsCounting)
|
||||
if (rcntCanCount(index))
|
||||
{
|
||||
if (counters[index].mode.ClockSource != 0x3)
|
||||
{
|
||||
|
@ -909,7 +888,7 @@ static __fi void rcntWmode(int index, u32 value)
|
|||
|
||||
counters[index].modeval &= ~(value & 0xc00);
|
||||
counters[index].modeval = (counters[index].modeval & 0xc00) | (value & 0x3ff);
|
||||
EECNT_LOG("EE Counter[%d] writeMode = %x passed value=%x", index, counters[index].modeval, value);
|
||||
EECNT_LOG("EE Counter[%d] writeMode = %x passed value=%x", index, counters[index].modeval, value);
|
||||
|
||||
switch (counters[index].mode.ClockSource) { //Clock rate divisers *2, they use BUSCLK speed not PS2CLK
|
||||
case 0: counters[index].rate = 2; break;
|
||||
|
@ -927,7 +906,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 (counters[index].mode.IsCounting)
|
||||
if (rcntCanCount(index))
|
||||
{
|
||||
if (counters[index].mode.ClockSource != 0x3)
|
||||
{
|
||||
|
@ -959,7 +938,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 (counters[index].mode.IsCounting)
|
||||
if (rcntCanCount(index))
|
||||
{
|
||||
if (counters[index].mode.ClockSource != 0x3)
|
||||
{
|
||||
|
@ -986,7 +965,7 @@ __fi u32 rcntRcount(int index)
|
|||
u32 ret;
|
||||
|
||||
// only count if the counter is turned on (0x80) and is not an hsync gate (!0x03)
|
||||
if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3))
|
||||
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;
|
||||
|
@ -1076,7 +1055,6 @@ bool SaveStateBase::rcntFreeze()
|
|||
Freeze(vSyncInfo);
|
||||
Freeze(gsVideoMode);
|
||||
Freeze(gsIsInterlaced);
|
||||
Freeze(gates);
|
||||
|
||||
if (IsLoading())
|
||||
cpuRcntSet();
|
||||
|
|
|
@ -22,9 +22,9 @@ struct EECNT_MODE
|
|||
u32 GateSource:1;
|
||||
|
||||
// 0 - count when the gate signal is low
|
||||
// 1 - reset and start counting at the signal's rising edge (h/v blank end)
|
||||
// 2 - reset and start counting at the signal's falling edge (h/v blank start)
|
||||
// 3 - reset and start counting at both signal edges
|
||||
// 1 - reset at the signal's rising edge (h/v blank start)
|
||||
// 2 - reset at the signal's falling edge (h/v blank end)
|
||||
// 3 - reset at both signal edges
|
||||
u32 GateMode:2;
|
||||
|
||||
// Counter cleared to zero when target reached.
|
||||
|
@ -126,6 +126,7 @@ extern uint g_FrameCount;
|
|||
|
||||
extern void rcntUpdate_hScanline();
|
||||
extern void rcntUpdate_vSync();
|
||||
extern bool rcntCanCount(int i);
|
||||
extern void rcntUpdate();
|
||||
|
||||
extern void rcntInit();
|
||||
|
|
|
@ -27,14 +27,16 @@
|
|||
HBlank 15.73426573 KHz */
|
||||
|
||||
// Misc IOP Clocks
|
||||
#define PSXPIXEL ((int)(PSXCLK / 13500000))
|
||||
// FIXME: this divider is actually 2.73 (36864000 / 13500000), but not sure what uses it, so this'll do, we should maybe change things to float.
|
||||
#define PSXPIXEL 3
|
||||
#define PSXSOUNDCLK ((int)(48000))
|
||||
|
||||
psxCounter psxCounters[NUM_COUNTERS];
|
||||
s32 psxNextCounter;
|
||||
u32 psxNextsCounter;
|
||||
u8 psxhblankgate = 0;
|
||||
u8 psxvblankgate = 0;
|
||||
|
||||
bool hBlanking = false;
|
||||
bool vBlanking = false;
|
||||
|
||||
// flags when the gate is off or counter disabled. (do not count)
|
||||
#define IOPCNT_STOPPED (0x10000000ul)
|
||||
|
@ -71,6 +73,32 @@ static void psxRcntReset(int index)
|
|||
}
|
||||
#endif
|
||||
|
||||
static bool psxRcntCanCount(int cntidx)
|
||||
{
|
||||
if (psxCounters[cntidx].mode & IOPCNT_STOPPED)
|
||||
return false;
|
||||
|
||||
if (!(psxCounters[cntidx].mode & IOPCNT_ENABLE_GATE))
|
||||
return true;
|
||||
|
||||
const u32 gateMode = (psxCounters[cntidx].mode & IOPCNT_MODE_GATE) >> 1;
|
||||
|
||||
if (cntidx == 2 || cntidx == 4 || cntidx == 5)
|
||||
{
|
||||
// Gates being enabled on these counters forces it to disable the counter if being on or off depends on a gate being on or off.
|
||||
return (gateMode & 1);
|
||||
}
|
||||
|
||||
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))
|
||||
return false;
|
||||
|
||||
// All other cases allow counting.
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _rcntSet(int cntidx)
|
||||
{
|
||||
u64 overflowCap = (cntidx >= 3) ? 0x100000000ULL : 0x10000;
|
||||
|
@ -83,7 +111,7 @@ static void _rcntSet(int cntidx)
|
|||
// that into account. Adding the difference from that cycle count to the current one
|
||||
// will do the trick!
|
||||
|
||||
if (counter.mode & IOPCNT_STOPPED || counter.rate == PSXHBLANK)
|
||||
if (counter.rate == PSXHBLANK || !psxRcntCanCount(cntidx))
|
||||
return;
|
||||
|
||||
if (!(counter.mode & (IOPCNT_INT_TARGET | IOPCNT_INT_OVERFLOW)))
|
||||
|
@ -256,6 +284,9 @@ Gate:
|
|||
TM_GATE_ON_Clear_OFF_Start 101
|
||||
TM_GATE_ON_Start 111
|
||||
|
||||
= means counting
|
||||
- means not counting
|
||||
|
||||
V-blank ----+ +----------------------------+ +------
|
||||
| | | |
|
||||
| | | |
|
||||
|
@ -266,19 +297,19 @@ Gate:
|
|||
|
||||
TM_GATE_ON_Count:
|
||||
|
||||
<---->0==========================><---->0=====
|
||||
<---->===========================><---->======
|
||||
|
||||
TM_GATE_ON_ClearStart:
|
||||
|
||||
0====>0================================>0=====
|
||||
=====>0================================>0=====
|
||||
|
||||
TM_GATE_ON_Clear_OFF_Start:
|
||||
|
||||
0====><-------------------------->0====><-----
|
||||
0====>0-------------------------->0====>0-----
|
||||
|
||||
TM_GATE_ON_Start:
|
||||
|
||||
<---->0==========================>============
|
||||
<---->===========================>============
|
||||
*/
|
||||
|
||||
static void _psxCheckStartGate(int i)
|
||||
|
@ -288,30 +319,32 @@ static void _psxCheckStartGate(int i)
|
|||
|
||||
switch ((psxCounters[i].mode & 0x6) >> 1)
|
||||
{
|
||||
case 0x0: // GATE_ON_count - stop count on gate start:
|
||||
case 0x0: // GATE_ON_count - count while gate signal is low (RENDER)
|
||||
|
||||
// get the current count at the time of stoppage:
|
||||
psxCounters[i].count = (i < 3) ?
|
||||
psxRcntRcount16(i) :
|
||||
psxRcntRcount32(i);
|
||||
psxCounters[i].mode |= IOPCNT_STOPPED;
|
||||
return;
|
||||
|
||||
case 0x1: // GATE_ON_ClearStart - count normally with resets after every end gate
|
||||
// do nothing - All counting will be done on a need-to-count basis.
|
||||
return;
|
||||
|
||||
case 0x2: // GATE_ON_Clear_OFF_Start - start counting on gate start, stop on gate end
|
||||
psxCounters[i].count = 0;
|
||||
// Not strictly necessary.
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
psxCounters[i].mode &= ~IOPCNT_STOPPED;
|
||||
break;
|
||||
|
||||
case 0x3: //GATE_ON_Start - start and count normally on gate end (no restarts or stops or clears)
|
||||
case 0x1: // GATE_ON_ClearStart - Counts constantly, clears on Blank END
|
||||
// do nothing!
|
||||
return;
|
||||
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;
|
||||
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.
|
||||
// do nothing!
|
||||
break;
|
||||
}
|
||||
_rcntSet(i);
|
||||
}
|
||||
|
||||
static void _psxCheckEndGate(int i)
|
||||
|
@ -319,105 +352,94 @@ static void _psxCheckEndGate(int i)
|
|||
if (!(psxCounters[i].mode & IOPCNT_ENABLE_GATE))
|
||||
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)
|
||||
{
|
||||
case 0x0: // GATE_ON_count - reset and start counting
|
||||
case 0x1: // GATE_ON_ClearStart - count normally with resets after every end gate
|
||||
psxCounters[i].count = 0;
|
||||
case 0x0: // GATE_ON_count - count while gate signal is low (RENDER)
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
psxCounters[i].mode &= ~IOPCNT_STOPPED;
|
||||
break;
|
||||
|
||||
case 0x2: // GATE_ON_Clear_OFF_Start - start counting on gate start, stop on gate end
|
||||
psxCounters[i].count = (i < 3) ? psxRcntRcount16(i) : psxRcntRcount32(i);
|
||||
psxCounters[i].mode |= IOPCNT_STOPPED;
|
||||
return; // do not set the counter
|
||||
case 0x1: // GATE_ON_ClearStart - Counts constantly, clears on Blank END
|
||||
psxCounters[i].count = 0;
|
||||
psxCounters[i].target &= ~IOPCNT_FUTURE_TARGET;
|
||||
break;
|
||||
|
||||
case 0x3: // GATE_ON_Start - start and count normally (no restarts or stops or clears)
|
||||
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.
|
||||
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)
|
||||
{
|
||||
psxCounters[i].count = 0;
|
||||
psxCounters[i].sCycleT = psxRegs.cycle;
|
||||
psxCounters[i].mode &= ~IOPCNT_STOPPED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
_rcntSet(i);
|
||||
}
|
||||
|
||||
void psxCheckStartGate16(int i)
|
||||
void psxHBlankStart()
|
||||
{
|
||||
pxAssert(i < 3);
|
||||
|
||||
if (i == 0) // hSync counting
|
||||
// AlternateSource/scanline counters for Gates 1 and 3.
|
||||
// We count them here so that they stay nicely synced with the EE's hsync.
|
||||
if ((psxCounters[1].rate == PSXHBLANK) && psxRcntCanCount(1))
|
||||
{
|
||||
// AlternateSource/scanline counters for Gates 1 and 3.
|
||||
// We count them here so that they stay nicely synced with the EE's hsync.
|
||||
|
||||
const u32 altSourceCheck = IOPCNT_ALT_SOURCE | IOPCNT_ENABLE_GATE;
|
||||
const u32 stoppedGateCheck = (IOPCNT_STOPPED | altSourceCheck);
|
||||
|
||||
// count if alt source is enabled and either:
|
||||
// * the gate is enabled and not stopped.
|
||||
// * the gate is disabled.
|
||||
|
||||
if ((psxCounters[1].mode & altSourceCheck) == IOPCNT_ALT_SOURCE ||
|
||||
(psxCounters[1].mode & stoppedGateCheck) == altSourceCheck)
|
||||
{
|
||||
psxCounters[1].count++;
|
||||
_rcntTestOverflow(1);
|
||||
_rcntTestTarget(1);
|
||||
}
|
||||
|
||||
if ((psxCounters[3].mode & altSourceCheck) == IOPCNT_ALT_SOURCE ||
|
||||
(psxCounters[3].mode & stoppedGateCheck) == altSourceCheck)
|
||||
{
|
||||
psxCounters[3].count++;
|
||||
_rcntTestOverflow(3);
|
||||
_rcntTestTarget(3);
|
||||
}
|
||||
psxCounters[1].count++;
|
||||
_rcntTestOverflow(1);
|
||||
_rcntTestTarget(1);
|
||||
}
|
||||
|
||||
_psxCheckStartGate(i);
|
||||
if ((psxCounters[3].rate == PSXHBLANK) && psxRcntCanCount(3))
|
||||
{
|
||||
psxCounters[3].count++;
|
||||
_rcntTestOverflow(3);
|
||||
_rcntTestTarget(3);
|
||||
}
|
||||
|
||||
_psxCheckStartGate(0);
|
||||
|
||||
hBlanking = true;
|
||||
|
||||
_rcntSet(0);
|
||||
}
|
||||
|
||||
void psxCheckEndGate16(int i)
|
||||
void psxHBlankEnd()
|
||||
{
|
||||
pxAssert(i < 3);
|
||||
_psxCheckEndGate(i);
|
||||
}
|
||||
_psxCheckEndGate(0);
|
||||
|
||||
static void psxCheckStartGate32(int i)
|
||||
{
|
||||
// 32 bit gate is called for gate 3 only. Ever.
|
||||
pxAssert(i == 3);
|
||||
_psxCheckStartGate(i);
|
||||
}
|
||||
hBlanking = false;
|
||||
|
||||
static void psxCheckEndGate32(int i)
|
||||
{
|
||||
pxAssert(i == 3);
|
||||
_psxCheckEndGate(i);
|
||||
_rcntSet(0);
|
||||
}
|
||||
|
||||
|
||||
void psxVBlankStart()
|
||||
{
|
||||
cdvdVsync();
|
||||
iopIntcIrq(0);
|
||||
if (psxvblankgate & (1 << 1))
|
||||
psxCheckStartGate16(1);
|
||||
if (psxvblankgate & (1 << 3))
|
||||
psxCheckStartGate32(3);
|
||||
|
||||
_psxCheckStartGate(1);
|
||||
_psxCheckStartGate(3);
|
||||
|
||||
vBlanking = true;
|
||||
|
||||
_rcntSet(1);
|
||||
_rcntSet(3);
|
||||
}
|
||||
|
||||
void psxVBlankEnd()
|
||||
{
|
||||
iopIntcIrq(11);
|
||||
if (psxvblankgate & (1 << 1))
|
||||
psxCheckEndGate16(1);
|
||||
if (psxvblankgate & (1 << 3))
|
||||
psxCheckEndGate32(3);
|
||||
|
||||
_psxCheckEndGate(1);
|
||||
_psxCheckEndGate(3);
|
||||
|
||||
vBlanking = false;
|
||||
|
||||
_rcntSet(1);
|
||||
_rcntSet(3);
|
||||
}
|
||||
|
||||
void psxRcntUpdate()
|
||||
|
@ -427,13 +449,13 @@ void psxRcntUpdate()
|
|||
psxNextCounter = 0x7fffffff;
|
||||
psxNextsCounter = psxRegs.cycle;
|
||||
|
||||
for (i = 0; i <= 5; i++)
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
// don't count disabled or hblank counters...
|
||||
// We can't check the ALTSOURCE flag because the PSXCLOCK source *should*
|
||||
// be counted here.
|
||||
|
||||
if (psxCounters[i].mode & IOPCNT_STOPPED)
|
||||
if (!psxRcntCanCount(i))
|
||||
continue;
|
||||
|
||||
if ((psxCounters[i].mode & IOPCNT_INT_REPEAT) && !(psxCounters[i].mode & IOPCNT_INT_TOGGLE))
|
||||
|
@ -448,39 +470,20 @@ void psxRcntUpdate()
|
|||
{
|
||||
const u32 change = (psxRegs.cycle - psxCounters[i].sCycleT) / psxCounters[i].rate;
|
||||
|
||||
if (change <= 0)
|
||||
continue;
|
||||
|
||||
psxCounters[i].count += change;
|
||||
psxCounters[i].sCycleT += change * 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Do target/overflow testing
|
||||
// Optimization Note: This approach is very sound. Please do not try to unroll it
|
||||
// as the size of the Test functions will cause code cache clutter and slowness.
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
// don't do target/oveflow checks for hblankers. Those
|
||||
// checks are done when the counters are updated.
|
||||
if (psxCounters[i].rate == PSXHBLANK)
|
||||
continue;
|
||||
if (psxCounters[i].mode & IOPCNT_STOPPED)
|
||||
continue;
|
||||
|
||||
_rcntTestOverflow(i);
|
||||
_rcntTestTarget(i);
|
||||
|
||||
// perform second target test because if we overflowed above it's possible we
|
||||
// already shot past our target if it was very near zero.
|
||||
|
||||
//if( psxCounters[i].count >= psxCounters[i].target ) _rcntTestTarget( i );
|
||||
}
|
||||
|
||||
const u32 spu2_delta = (psxRegs.cycle - lClocks) % 768;
|
||||
|
@ -615,11 +618,6 @@ __fi void psxRcntWmode16(int index, u32 value)
|
|||
break;
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
|
||||
if ((counter.mode & 0x7) == 0x7 || (counter.mode & 0x7) == 0x1)
|
||||
{
|
||||
counter.mode |= IOPCNT_STOPPED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -629,26 +627,22 @@ __fi void psxRcntWmode16(int index, u32 value)
|
|||
if (value & IOPCNT_ALT_SOURCE)
|
||||
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)
|
||||
{
|
||||
// gated counters are added up as per the h/vblank timers.
|
||||
// (the PIXEL alt source becomes a vsync gate)
|
||||
counter.mode |= IOPCNT_STOPPED;
|
||||
// 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;
|
||||
|
||||
PSXCNT_LOG("IOP Counter[%d] Gate Check set, value = 0x%04X", index, value);
|
||||
if (index == 0)
|
||||
psxhblankgate |= 1; // fixme: these gate flags should be one var >_<
|
||||
else
|
||||
psxvblankgate |= 1 << 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index == 0)
|
||||
psxhblankgate &= ~1;
|
||||
else
|
||||
psxvblankgate &= ~(1 << 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Current counter *always* resets on mode write.
|
||||
counter.count = 0;
|
||||
counter.sCycleT = psxRegs.cycle;
|
||||
|
||||
|
@ -703,11 +697,10 @@ __fi void psxRcntWmode32(int index, u32 value)
|
|||
if (counter.mode & IOPCNT_ENABLE_GATE)
|
||||
{
|
||||
PSXCNT_LOG("IOP Counter[3] Gate Check set, value = %x", value);
|
||||
counter.mode |= IOPCNT_STOPPED;
|
||||
psxvblankgate |= 1 << 3;
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
psxvblankgate &= ~(1 << 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -726,15 +719,9 @@ __fi void psxRcntWmode32(int index, u32 value)
|
|||
counter.rate = 256;
|
||||
break;
|
||||
}
|
||||
|
||||
// Need to set a rate and target
|
||||
if ((counter.mode & 0x7) == 0x7 || (counter.mode & 0x7) == 0x1)
|
||||
{
|
||||
Console.WriteLn("Gate set on IOP Counter %d, disabling", index);
|
||||
counter.mode |= IOPCNT_STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
// Current counter *always* resets on mode write.
|
||||
counter.count = 0;
|
||||
counter.sCycleT = psxRegs.cycle;
|
||||
counter.target &= 0xffffffff;
|
||||
|
@ -755,7 +742,7 @@ void psxRcntWtarget16(int index, u32 value)
|
|||
psxCounters[index].mode |= IOPCNT_INT_REQ; // Interrupt flag reset to high
|
||||
}
|
||||
|
||||
if (!(psxCounters[index].mode & IOPCNT_STOPPED) &&
|
||||
if (psxRcntCanCount(index) &&
|
||||
(psxCounters[index].rate != PSXHBLANK))
|
||||
{
|
||||
// Re-adjust the sCycleT to match where the counter is currently
|
||||
|
@ -789,7 +776,7 @@ void psxRcntWtarget32(int index, u32 value)
|
|||
psxCounters[index].mode |= IOPCNT_INT_REQ; // Interrupt flag reset to high
|
||||
}
|
||||
|
||||
if (!(psxCounters[index].mode & IOPCNT_STOPPED) &&
|
||||
if (psxRcntCanCount(index) &&
|
||||
(psxCounters[index].rate != PSXHBLANK))
|
||||
{
|
||||
// Re-adjust the sCycleT to match where the counter is currently
|
||||
|
@ -819,14 +806,15 @@ u16 psxRcntRcount16(int index)
|
|||
|
||||
// Don't count HBLANK timers
|
||||
// Don't count stopped gates either.
|
||||
|
||||
if (!(psxCounters[index].mode & IOPCNT_STOPPED) &&
|
||||
(psxCounters[index].rate != PSXHBLANK))
|
||||
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;
|
||||
}
|
||||
|
@ -839,35 +827,19 @@ u32 psxRcntRcount32(int index)
|
|||
|
||||
PSXCNT_LOG("IOP Counter[%d] readCount32 = %lx", index, retval);
|
||||
|
||||
if (!(psxCounters[index].mode & IOPCNT_STOPPED) &&
|
||||
(psxCounters[index].rate != PSXHBLANK))
|
||||
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 psxRcntSetGates()
|
||||
{
|
||||
if (psxCounters[0].mode & IOPCNT_ENABLE_GATE)
|
||||
psxhblankgate |= 1;
|
||||
else
|
||||
psxhblankgate &= ~1;
|
||||
|
||||
if (psxCounters[1].mode & IOPCNT_ENABLE_GATE)
|
||||
psxvblankgate |= 1 << 1;
|
||||
else
|
||||
psxvblankgate &= ~(1 << 1);
|
||||
|
||||
if (psxCounters[3].mode & IOPCNT_ENABLE_GATE)
|
||||
psxvblankgate |= 1 << 3;
|
||||
else
|
||||
psxvblankgate &= ~(1 << 3);
|
||||
}
|
||||
|
||||
bool SaveStateBase::psxRcntFreeze()
|
||||
{
|
||||
if (!FreezeTag("iopCounters"))
|
||||
|
@ -876,8 +848,8 @@ bool SaveStateBase::psxRcntFreeze()
|
|||
Freeze(psxCounters);
|
||||
Freeze(psxNextCounter);
|
||||
Freeze(psxNextsCounter);
|
||||
Freeze(psxvblankgate);
|
||||
Freeze(psxhblankgate);
|
||||
Freeze(hBlanking);
|
||||
Freeze(vBlanking);
|
||||
|
||||
if (!IsOkay())
|
||||
return false;
|
||||
|
|
|
@ -29,7 +29,7 @@ extern u16 psxRcntRcount16(int index);
|
|||
extern u32 psxRcntRcount32(int index);
|
||||
extern u64 psxRcntCycles(int index);
|
||||
|
||||
extern void psxHBlankStart();
|
||||
extern void psxHBlankEnd();
|
||||
extern void psxVBlankStart();
|
||||
extern void psxVBlankEnd();
|
||||
extern void psxCheckStartGate16(int i);
|
||||
extern void psxCheckEndGate16(int i);
|
||||
|
|
|
@ -370,11 +370,33 @@ __fi void _cpuEventTest_Shared()
|
|||
if (cpuIntsEnabled(mask))
|
||||
cpuException(mask, cpuRegs.branch);
|
||||
|
||||
// ---- IOP -------------
|
||||
// * It's important to run a iopEventTest before calling ExecuteBlock. This
|
||||
// is because the IOP does not always perform branch tests before returning
|
||||
// (during the prev branch) and also so it can act on the state the EE has
|
||||
// given it before executing any code.
|
||||
//
|
||||
// * The IOP cannot always be run. If we run IOP code every time through the
|
||||
// cpuEventTest, the IOP generally starts to run way ahead of the EE.
|
||||
|
||||
// ---- Counters -------------
|
||||
// Important: the vsync counter must be the first to be checked. It includes emulation
|
||||
// escape/suspend hooks, and it's really a good idea to suspend/resume emulation before
|
||||
// doing any actual meaningful branchtest logic.
|
||||
// It's also important to sync up the IOP before updating the timers, since gates will depend on starting/stopping in the right place!
|
||||
EEsCycle += cpuRegs.cycle - EEoCycle;
|
||||
EEoCycle = cpuRegs.cycle;
|
||||
|
||||
if (EEsCycle > 0)
|
||||
iopEventAction = true;
|
||||
|
||||
if (iopEventAction)
|
||||
{
|
||||
//if( EEsCycle < -450 )
|
||||
// Console.WriteLn( " IOP ahead by: %d cycles", -EEsCycle );
|
||||
|
||||
EEsCycle = psxCpu->ExecuteBlock(EEsCycle);
|
||||
|
||||
iopEventAction = false;
|
||||
}
|
||||
|
||||
iopEventTest();
|
||||
|
||||
if (cpuTestCycle(nextsCounter, nextCounter))
|
||||
{
|
||||
|
@ -403,33 +425,6 @@ __fi void _cpuEventTest_Shared()
|
|||
_cpuTestInterrupts();
|
||||
}
|
||||
|
||||
// ---- IOP -------------
|
||||
// * It's important to run a iopEventTest before calling ExecuteBlock. This
|
||||
// is because the IOP does not always perform branch tests before returning
|
||||
// (during the prev branch) and also so it can act on the state the EE has
|
||||
// given it before executing any code.
|
||||
//
|
||||
// * The IOP cannot always be run. If we run IOP code every time through the
|
||||
// cpuEventTest, the IOP generally starts to run way ahead of the EE.
|
||||
|
||||
EEsCycle += cpuRegs.cycle - EEoCycle;
|
||||
EEoCycle = cpuRegs.cycle;
|
||||
|
||||
if (EEsCycle > 0)
|
||||
iopEventAction = true;
|
||||
|
||||
if (iopEventAction)
|
||||
{
|
||||
//if( EEsCycle < -450 )
|
||||
// Console.WriteLn( " IOP ahead by: %d cycles", -EEsCycle );
|
||||
|
||||
EEsCycle = psxCpu->ExecuteBlock(EEsCycle);
|
||||
|
||||
iopEventAction = false;
|
||||
}
|
||||
|
||||
iopEventTest();
|
||||
|
||||
// ---- VU Sync -------------
|
||||
// We're in a EventTest. All dynarec registers are flushed
|
||||
// so there is no need to freeze registers here.
|
||||
|
|
|
@ -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 = (0x9A4B << 16) | 0x0000;
|
||||
static const u32 g_SaveVersion = (0x9A4C << 16) | 0x0000;
|
||||
|
||||
|
||||
// the freezing data between submodules and core
|
||||
|
|
Loading…
Reference in New Issue