Counters: Improve counter updates and fix a couple of errors

This commit is contained in:
refractionpcsx2 2023-11-04 03:19:44 +00:00
parent 65d4baa944
commit b824c2d31a
5 changed files with 248 additions and 226 deletions

View File

@ -73,7 +73,8 @@ static bool IsProgressiveVideoMode()
return !(*(u32*)PS2GS_BASE(GS_SYNCV) & 0x1) || !(*(u32*)PS2GS_BASE(GS_SMODE1) & 0x6000);
}
void rcntReset(int index) {
void rcntReset(int index)
{
counters[index].count = 0;
counters[index].sCycleT = cpuRegs.cycle;
}
@ -81,20 +82,22 @@ void rcntReset(int index) {
// Updates the state of the nextCounter value (if needed) to serve
// any pending events for the given counter.
// Call this method after any modifications to the state of a counter.
static __fi void _rcntSet( int cntidx )
static __fi void _rcntSet(int cntidx)
{
s32 c;
pxAssume( cntidx <= 4 ); // rcntSet isn't valid for h/vsync counters.
pxAssume(cntidx <= 4); // rcntSet isn't valid for h/vsync counters.
const Counter& counter = counters[cntidx];
// Stopped or special hsync gate?
if (!counter.mode.IsCounting || (counter.mode.ClockSource == 0x3) ) return;
if (!counter.mode.IsCounting || (counter.mode.ClockSource == 0x3))
return;
if (!counter.mode.TargetInterrupt && !counter.mode.OverflowInterrupt) return;
if (!counter.mode.TargetInterrupt && !counter.mode.OverflowInterrupt)
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 )
if (counter.count > 0x10000 || counter.count > counter.target)
{
nextCounter = 4;
return;
@ -106,26 +109,27 @@ static __fi void _rcntSet( int cntidx )
// 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();
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
if (c < nextCounter)
{
nextCounter = c;
cpuSetNextEvent( nextsCounter, nextCounter ); // Need to update on counter resets/target changes
cpuSetNextEvent(nextsCounter, nextCounter); // Need to update on counter resets/target changes
}
// Ignore target diff if target is currently disabled.
// (the overflow is all we care about since it goes first, and then the
// target will be turned on afterward, and handled in the next event test).
if( counter.target & EECNT_FUTURE_TARGET )
if (counter.target & EECNT_FUTURE_TARGET)
{
return;
}
else
{
c = ((counter.target - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
if (c < nextCounter)
{
nextCounter = c;
@ -149,7 +153,7 @@ static __fi void cpuRcntSet()
nextCounter = nextHsync;
for (i = 0; i < 4; i++)
_rcntSet( i );
_rcntSet(i);
// sanity check!
if (nextCounter < 0)
@ -166,11 +170,12 @@ void rcntInit()
std::memset(counters, 0, sizeof(counters));
for (i=0; i<4; i++) {
for (i = 0; i < 4; i++)
{
counters[i].rate = 2;
counters[i].target = 0xffff;
}
counters[0].interrupt = 9;
counters[0].interrupt = 9;
counters[1].interrupt = 10;
counters[2].interrupt = 11;
counters[3].interrupt = 12;
@ -180,7 +185,8 @@ void rcntInit()
vsyncCounter.Mode = MODE_VRENDER;
vsyncCounter.sCycle = cpuRegs.cycle;
for (i=0; i<4; i++) rcntReset(i);
for (i = 0; i < 4; i++)
rcntReset(i);
cpuRcntSet();
}
@ -246,11 +252,15 @@ static void vSyncInfoCalc(vSyncTimingInfo* info, double framesPerSecond, u32 sca
info->hBlank = (u32)(hBlank / 10000);
info->hScanlinesPerFrame = scansPerFrame;
if ((Render % 10000) >= 5000) info->Render++;
if ((Blank % 10000) >= 5000) info->Blank++;
if ((Render % 10000) >= 5000)
info->Render++;
if ((Blank % 10000) >= 5000)
info->Blank++;
if ((hRender % 10000) >= 5000) info->hRender++;
if ((hBlank % 10000) >= 5000) info->hBlank++;
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
@ -260,7 +270,8 @@ static void vSyncInfoCalc(vSyncTimingInfo* info, double framesPerSecond, u32 sca
info->hSyncError = vSyncCycles - hSyncCycles;
//Console.Warning("%d",info->hSyncError);
}
else info->hSyncError = 0;
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.
@ -316,25 +327,25 @@ double GetVerticalFrequency()
switch (gsVideoMode)
{
case GS_VideoMode::Uninitialized: // SetGsCrt hasn't executed yet, give some temporary values.
return 60.00;
case GS_VideoMode::PAL:
case GS_VideoMode::DVD_PAL:
return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FrameratePAL : EmuConfig.GS.FrameratePAL - 0.24f;
case GS_VideoMode::NTSC:
case GS_VideoMode::DVD_NTSC:
return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FramerateNTSC : EmuConfig.GS.FramerateNTSC - 0.11f;
case GS_VideoMode::SDTV_480P:
return 59.94;
case GS_VideoMode::HDTV_1080P:
case GS_VideoMode::HDTV_1080I:
case GS_VideoMode::HDTV_720P:
case GS_VideoMode::SDTV_576P:
case GS_VideoMode::VESA:
return 60.00;
default:
// Pass NTSC vertical frequency value when unknown video mode is detected.
return FRAMERATE_NTSC * 2;
case GS_VideoMode::Uninitialized: // SetGsCrt hasn't executed yet, give some temporary values.
return 60.00;
case GS_VideoMode::PAL:
case GS_VideoMode::DVD_PAL:
return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FrameratePAL : EmuConfig.GS.FrameratePAL - 0.24f;
case GS_VideoMode::NTSC:
case GS_VideoMode::DVD_NTSC:
return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FramerateNTSC : EmuConfig.GS.FramerateNTSC - 0.11f;
case GS_VideoMode::SDTV_480P:
return 59.94;
case GS_VideoMode::HDTV_1080P:
case GS_VideoMode::HDTV_1080I:
case GS_VideoMode::HDTV_720P:
case GS_VideoMode::SDTV_576P:
case GS_VideoMode::VESA:
return 60.00;
default:
// Pass NTSC vertical frequency value when unknown video mode is detected.
return FRAMERATE_NTSC * 2;
}
}
@ -498,13 +509,14 @@ static __fi void VSyncStart(u32 sCycle)
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount );
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();
if (gates) rcntStartGate(true, sCycle); // Counters Start Gate code
if (gates)
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
@ -554,14 +566,15 @@ static __fi void GSVSync()
static __fi void VSyncEnd(u32 sCycle)
{
if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC END (frame: %d) ================", g_FrameCount );
if (EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
SysTrace.EE.Counters.Write(" ================ EE COUNTER VSYNC END (frame: %d) ================", g_FrameCount);
g_FrameCount++;
hwIntcIrq(INTC_VBLANK_E); // HW Irq
hwIntcIrq(INTC_VBLANK_E); // HW Irq
psxVBlankEnd(); // psxCounters vBlank End
if (gates) rcntEndGate(true, sCycle); // Counters End Gate Code
if (gates)
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
@ -575,48 +588,54 @@ static __fi void VSyncEnd(u32 sCycle)
//#define VSYNC_DEBUG // Uncomment this to enable some vSync Timer debugging features.
#ifdef VSYNC_DEBUG
static u32 hsc=0;
static u32 hsc = 0;
static int vblankinc = 0;
#endif
__fi void rcntUpdate_hScanline()
{
if( !cpuTestCycle( hsyncCounter.sCycle, hsyncCounter.CycleT ) ) return;
if (!cpuTestCycle(hsyncCounter.sCycle, hsyncCounter.CycleT))
return;
//iopEventAction = 1;
if (hsyncCounter.Mode == MODE_HBLANK) { //HBLANK Start
if (hsyncCounter.Mode == MODE_HBLANK)
{ //HBLANK Start
rcntStartGate(false, hsyncCounter.sCycle);
psxCheckStartGate16(0);
// 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.sCycle += vSyncInfo.hBlank; // start (absolute cycle value)
hsyncCounter.CycleT = vSyncInfo.hRender; // endpoint (delta from start value)
hsyncCounter.Mode = MODE_HRENDER;
}
else { //HBLANK END / HRENDER Begin
else
{ //HBLANK END / HRENDER Begin
if (!CSRreg.HSINT)
{
CSRreg.HSINT = true;
if (!GSIMR.HSMSK)
gsIrq();
}
if (gates) rcntEndGate(false, hsyncCounter.sCycle);
if (psxhblankgate) psxCheckEndGate16(0);
if (gates)
rcntEndGate(false, hsyncCounter.sCycle);
if (psxhblankgate)
psxCheckEndGate16(0);
// 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.sCycle += vSyncInfo.hRender; // start (absolute cycle value)
hsyncCounter.CycleT = vSyncInfo.hBlank; // endpoint (delta from start value)
hsyncCounter.Mode = MODE_HBLANK;
# ifdef VSYNC_DEBUG
#ifdef VSYNC_DEBUG
hsc++;
# endif
#endif
}
}
__fi void rcntUpdate_vSync()
{
if (!cpuTestCycle(vsyncCounter.sCycle, vsyncCounter.CycleT)) return;
if (!cpuTestCycle(vsyncCounter.sCycle, vsyncCounter.CycleT))
return;
if (vsyncCounter.Mode == MODE_VSYNC)
{
@ -634,7 +653,7 @@ __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 end / VRENDER begin
{
VSyncStart(vsyncCounter.sCycle);
@ -645,25 +664,26 @@ __fi void rcntUpdate_vSync()
// Accumulate hsync rounding errors:
hsyncCounter.sCycle += vSyncInfo.hSyncError;
# ifdef VSYNC_DEBUG
#ifdef VSYNC_DEBUG
vblankinc++;
if( vblankinc > 1 )
if (vblankinc > 1)
{
if( hsc != vSyncInfo.hScanlinesPerFrame )
Console.WriteLn( " ** vSync > Abnormal Scanline Count: %d", hsc );
if (hsc != vSyncInfo.hScanlinesPerFrame)
Console.WriteLn(" ** vSync > Abnormal Scanline Count: %d", hsc);
hsc = 0;
vblankinc = 0;
}
# endif
#endif
}
}
static __fi void _cpuTestTarget( int i )
static __fi void _cpuTestTarget(int i)
{
if (counters[i].count < counters[i].target)
return;
if(counters[i].mode.TargetInterrupt) {
if (counters[i].mode.TargetInterrupt)
{
EECNT_LOG("EE Counter[%d] TARGET reached - mode=%x, count=%x, target=%x", i, counters[i].mode, counters[i].count, counters[i].target);
if (!counters[i].mode.TargetReached)
{
@ -678,11 +698,13 @@ static __fi void _cpuTestTarget( int i )
counters[i].target |= EECNT_FUTURE_TARGET; // OR with future target to prevent a retrigger
}
static __fi void _cpuTestOverflow( int i )
static __fi void _cpuTestOverflow(int i)
{
if (counters[i].count <= 0xffff) return;
if (counters[i].count <= 0xffff)
return;
if (counters[i].mode.OverflowInterrupt) {
if (counters[i].mode.OverflowInterrupt)
{
EECNT_LOG("EE Counter[%d] OVERFLOW - mode=%x, count=%x", i, counters[i].mode, counters[i].count);
if (!counters[i].mode.OverflowReached)
{
@ -708,57 +730,56 @@ __fi void rcntUpdate()
// Update counters so that we can perform overflow and target tests.
for (int i=0; i<=3; i++)
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 ) continue;
if (!counters[i].mode.IsCounting)
continue;
if(counters[i].mode.ClockSource != 0x3) // don't count hblank sources
if (counters[i].mode.ClockSource != 0x3) // don't count hblank sources
{
s32 change = cpuRegs.cycle - counters[i].sCycleT;
if( change < 0 ) change = 0; // sanity check!
counters[i].count += change / counters[i].rate;
change -= (change / counters[i].rate) * counters[i].rate;
counters[i].sCycleT = cpuRegs.cycle - change;
const u32 change = (cpuRegs.cycle - counters[i].sCycleT) / counters[i].rate;
counters[i].count += change;
counters[i].sCycleT += change * counters[i].rate;
// Check Counter Targets and Overflows:
// Check Overflow first, in case the target is 0
_cpuTestOverflow( i );
_cpuTestOverflow(i);
_cpuTestTarget(i);
}
else counters[i].sCycleT = cpuRegs.cycle;
else
counters[i].sCycleT = cpuRegs.cycle;
}
cpuRcntSet();
}
static __fi void _rcntSetGate( int index )
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.
if( !(counters[index].mode.GateSource == 0 && counters[index].mode.ClockSource == 3) )
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 );
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);
gates |= (1 << index);
counters[index].mode.IsCounting = 0;
rcntReset(index);
return;
}
else
EECNT_LOG( "EE Counter[%d] GATE DISABLED because of hblank source.", index );
EECNT_LOG("EE Counter[%d] GATE DISABLED because of hblank source.", index);
}
gates &= ~(1<<index);
gates &= ~(1 << index);
}
// mode - 0 means hblank source, 8 means vblank source.
@ -766,10 +787,10 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
{
int i;
for (i=0; i <=3; i++)
for (i = 0; i <= 3; 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.IsCounting && (counters[i].mode.ClockSource == 3))
{
// 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
@ -780,13 +801,16 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
counters[i].count += HBLANK_COUNTER_SPEED;
_cpuTestOverflow(i);
_cpuTestTarget( i );
_cpuTestTarget(i);
}
if (!(gates & (1<<i))) continue;
if ((!!counters[i].mode.GateSource) != isVblank) continue;
if (!(gates & (1 << i)))
continue;
if ((!!counters[i].mode.GateSource) != isVblank)
continue;
switch (counters[i].mode.GateMode) {
switch (counters[i].mode.GateMode)
{
case 0x0: //Count When Signal is low (off)
// Just set the start cycle (sCycleT) -- counting will be done as needed
@ -796,10 +820,10 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
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 );
isVblank ? "vblank" : "hblank", counters[i].count);
break;
case 0x2: // reset and start counting on vsync end
case 0x2: // reset and start counting on vsync end
// this is the vsync start so do nothing.
break;
@ -810,7 +834,7 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
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 );
isVblank ? "vblank" : "hblank", counters[i].mode.GateMode, counters[i].count);
break;
}
}
@ -824,15 +848,19 @@ 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)
static __fi void rcntEndGate(bool isVblank, u32 sCycle)
{
int i;
for(i=0; i <=3; i++) { //Gates for counters
if (!(gates & (1<<i))) continue;
if ((!!counters[i].mode.GateSource) != isVblank) continue;
for (i = 0; i <= 3; i++)
{ //Gates for counters
if (!(gates & (1 << i)))
continue;
if ((!!counters[i].mode.GateSource) != isVblank)
continue;
switch (counters[i].mode.GateMode) {
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
@ -842,12 +870,12 @@ static __fi void rcntEndGate(bool isVblank , u32 sCycle)
counters[i].sCycleT = cpuRegs.cycle;
EECNT_LOG("EE Counter[%d] %s EndGate Type0, count = %x", i,
isVblank ? "vblank" : "hblank", counters[i].count );
break;
isVblank ? "vblank" : "hblank", counters[i].count);
break;
case 0x1: // Reset and start counting on Vsync start
case 0x1: // Reset and start counting on Vsync start
// this is the vsync end so do nothing
break;
break;
case 0x2: //Reset and start counting on Vsync end
case 0x3: //Reset and start counting on Vsync start and end
@ -856,44 +884,34 @@ static __fi void rcntEndGate(bool isVblank , u32 sCycle)
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;
isVblank ? "vblank" : "hblank", counters[i].mode.GateMode, counters[i].count);
break;
}
}
// Note: No need to set counters here. They'll get set when control returns to
// rcntUpdate, since we're being called from there anyway.
}
static __fi u32 rcntCycle(int index)
{
if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3))
return counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate);
else
return counters[index].count;
}
static __fi void rcntWmode(int index, u32 value)
{
if(counters[index].mode.IsCounting) {
if(counters[index].mode.ClockSource != 0x3) {
u32 change = cpuRegs.cycle - counters[index].sCycleT;
if( change > 0 )
{
counters[index].count += change / counters[index].rate;
change -= (change / counters[index].rate) * counters[index].rate;
counters[index].sCycleT = cpuRegs.cycle - change;
}
if (counters[index].mode.IsCounting)
{
if (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;
else
counters[index].sCycleT = cpuRegs.cycle;
// 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).
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;
@ -902,34 +920,35 @@ static __fi void rcntWmode(int index, u32 value)
case 3: counters[index].rate = vSyncInfo.hBlank+vSyncInfo.hRender; break;
}
_rcntSetGate( index );
_rcntSet( index );
_rcntSetGate(index);
_rcntSet(index);
}
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 );
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 (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;
counters[index].count = value & 0xffff;
// reset the target, and make sure we don't get a premature target.
counters[index].target &= 0xffff;
if( counters[index].count > counters[index].target )
if (counters[index].count > counters[index].target)
counters[index].target |= EECNT_FUTURE_TARGET;
// re-calculate the start cycle of the counter based on elapsed time since the last counter update:
if(counters[index].mode.IsCounting) {
if(counters[index].mode.ClockSource != 0x3) {
s32 change = cpuRegs.cycle - counters[index].sCycleT;
if( change > 0 ) {
change -= (change / counters[index].rate) * counters[index].rate;
counters[index].sCycleT = cpuRegs.cycle - change;
}
}
}
else counters[index].sCycleT = cpuRegs.cycle;
_rcntSet( index );
_rcntSet(index);
}
static __fi void rcntWtarget(int index, u32 value)
@ -942,23 +961,20 @@ 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(counters[index].mode.ClockSource != 0x3) {
u32 change = cpuRegs.cycle - counters[index].sCycleT;
if( change > 0 )
{
counters[index].count += change / counters[index].rate;
change -= (change / counters[index].rate) * counters[index].rate;
counters[index].sCycleT = cpuRegs.cycle - change;
}
if (counters[index].mode.IsCounting)
{
if (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;
}
}
if( counters[index].target <= rcntCycle(index) )
if (counters[index].target <= counters[index].count)
counters[index].target |= EECNT_FUTURE_TARGET;
_rcntSet( index );
_rcntSet(index);
}
static __fi void rcntWhold(int index, u32 value)
@ -982,8 +998,8 @@ __fi u32 rcntRcount(int index)
return ret;
}
template< uint page >
__fi u16 rcntRead32( u32 mem )
template <uint page>
__fi u16 rcntRead32(u32 mem)
{
// Important DevNote:
// Yes this uses a u16 return value on purpose! The upper bits 16 of the counter registers
@ -1013,10 +1029,10 @@ __fi u16 rcntRead32( u32 mem )
return psHu16(mem);
}
template< uint page >
__fi bool rcntWrite32( u32 mem, mem32_t& value )
template <uint page>
__fi bool rcntWrite32(u32 mem, mem32_t& value)
{
pxAssume( mem >= RCNT0_COUNT && mem < 0x10002000 );
pxAssume(mem >= RCNT0_COUNT && mem < 0x10002000);
// [TODO] : counters should actually just use the EE's hw register space for storing
// count, mode, target, and hold. This will allow for a simplified handler for register
@ -1046,25 +1062,25 @@ __fi bool rcntWrite32( u32 mem, mem32_t& value )
return true;
}
template u16 rcntRead32<0x00>( u32 mem );
template u16 rcntRead32<0x01>( u32 mem );
template u16 rcntRead32<0x00>(u32 mem);
template u16 rcntRead32<0x01>(u32 mem);
template bool rcntWrite32<0x00>( u32 mem, mem32_t& value );
template bool rcntWrite32<0x01>( u32 mem, mem32_t& value );
template bool rcntWrite32<0x00>(u32 mem, mem32_t& value);
template bool rcntWrite32<0x01>(u32 mem, mem32_t& value);
bool SaveStateBase::rcntFreeze()
{
Freeze( counters );
Freeze( hsyncCounter );
Freeze( vsyncCounter );
Freeze( nextCounter );
Freeze( nextsCounter );
Freeze( vSyncInfo );
Freeze( gsVideoMode );
Freeze( gsIsInterlaced );
Freeze( gates );
Freeze(counters);
Freeze(hsyncCounter);
Freeze(vsyncCounter);
Freeze(nextCounter);
Freeze(nextsCounter);
Freeze(vSyncInfo);
Freeze(gsVideoMode);
Freeze(gsIsInterlaced);
Freeze(gates);
if( IsLoading() )
if (IsLoading())
cpuRcntSet();
return IsOkay();

View File

@ -161,7 +161,7 @@ void psxRcntInit()
psxCounters[4].interrupt = 0x08000;
psxCounters[5].interrupt = 0x10000;
psxCounters[6].rate = 768 * 12; // 12 SPU ticks. 768 would be ideal but some games slow down internally for some reason
psxCounters[6].rate = 768;
psxCounters[6].CycleT = psxCounters[6].rate;
psxCounters[6].mode = 0x8;
@ -312,7 +312,7 @@ static void _psxCheckStartGate(int i)
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.
// 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
@ -322,7 +322,7 @@ static void _psxCheckStartGate(int i)
break;
case 0x3: //GATE_ON_Start - start and count normally on gate end (no restarts or stops or clears)
// do nothing!
// do nothing!
return;
}
_rcntSet(i);
@ -443,8 +443,6 @@ void psxRcntUpdate()
for (i = 0; i <= 5; i++)
{
s32 change = psxRegs.cycle - psxCounters[i].sCycleT;
// don't count disabled or hblank counters...
// We can't check the ALTSOURCE flag because the PSXCLOCK source *should*
// be counted here.
@ -460,17 +458,21 @@ void psxRcntUpdate()
if (psxCounters[i].rate == PSXHBLANK)
continue;
if (change <= 0)
continue;
psxCounters[i].count += change / psxCounters[i].rate;
if (psxCounters[i].rate != 1)
{
change -= (change / psxCounters[i].rate) * psxCounters[i].rate;
psxCounters[i].sCycleT = psxRegs.cycle - change;
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;
}
else
{
psxCounters[i].count += psxRegs.cycle - psxCounters[i].sCycleT;
psxCounters[i].sCycleT = psxRegs.cycle;
}
}
// Do target/overflow testing
@ -499,16 +501,12 @@ void psxRcntUpdate()
const s32 difference = psxRegs.cycle - psxCounters[6].sCycleT;
s32 c = psxCounters[6].CycleT;
if (difference >= psxCounters[6].CycleT)
{
psxCounters[6].sCycleT = psxRegs.cycle;
psxCounters[6].CycleT = psxCounters[6].rate;
SPU2async(difference);
c = psxCounters[6].CycleT;
}
else
c -= difference;
psxNextCounter = c;
const u32 spu2_delta = (psxRegs.cycle - lClocks) % 768;
psxCounters[6].sCycleT = psxRegs.cycle;
psxCounters[6].CycleT = psxCounters[6].rate - spu2_delta;
SPU2async();
psxNextCounter = psxCounters[6].CycleT;
DEV9async(1);
const s32 diffusb = psxRegs.cycle - psxCounters[7].sCycleT;
s32 cusb = psxCounters[7].CycleT;
@ -516,7 +514,7 @@ void psxRcntUpdate()
if (diffusb >= psxCounters[7].CycleT)
{
USBasync(diffusb);
psxCounters[7].sCycleT = psxRegs.cycle;
psxCounters[7].sCycleT += psxCounters[7].rate * (diffusb / psxCounters[7].rate);
psxCounters[7].CycleT = psxCounters[7].rate;
}
else
@ -533,30 +531,25 @@ void psxRcntUpdate()
//
void psxRcntWcount16(int index, u16 value)
{
u32 change;
pxAssert(index < 3);
//DevCon.Warning("16bit IOP Counter[%d] writeCount16 = %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)
change = psxRegs.cycle - psxCounters[index].sCycleT;
psxCounters[index].sCycleT = psxRegs.cycle - (change % psxCounters[index].rate);
const u32 change = (psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate;
psxCounters[index].sCycleT += change * psxCounters[index].rate;
}
psxCounters[index].count = value & 0xffff;
psxCounters[index].target &= 0xffff;
if (psxCounters[index].count > psxCounters[index].target)
{
// Count already higher than Target
//DevCon.Warning("32bit Count already higher than target");
psxCounters[index].target |= IOPCNT_FUTURE_TARGET;
}
else
psxCounters[index].target &= 0xffff;
_rcntSet(index);
}
@ -565,8 +558,6 @@ void psxRcntWcount16(int index, u16 value)
//
void psxRcntWcount32(int index, u32 value)
{
u32 change;
pxAssert(index >= 3 && index < 6);
PSXCNT_LOG("32bit IOP Counter[%d] writeCount32 = %x", index, value);
@ -575,20 +566,20 @@ void psxRcntWcount32(int index, u32 value)
// Re-adjust the sCycleT to match where the counter is currently
// (remainder of the rate divided into the time passed will do the trick)
change = psxRegs.cycle - psxCounters[index].sCycleT;
psxCounters[index].sCycleT = psxRegs.cycle - (change % psxCounters[index].rate);
const u32 change = (psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate;
psxCounters[index].sCycleT += change * psxCounters[index].rate;
}
psxCounters[index].count = value;
psxCounters[index].target &= 0xffffffff;
if (psxCounters[index].count > psxCounters[index].target)
{
// Count already higher than Target
//DevCon.Warning("32bit Count already higher than target");
psxCounters[index].target |= IOPCNT_FUTURE_TARGET;
}
else
psxCounters[index].target &= 0xffffffff;
_rcntSet(index);
}
@ -781,11 +772,23 @@ void psxRcntWtarget16(int index, u32 value)
// Pulse mode reset
psxCounters[index].mode |= IOPCNT_INT_REQ; // Interrupt flag reset to high
}
if (!(psxCounters[index].mode & IOPCNT_STOPPED) &&
(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;
}
// 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.
if (psxCounters[index].target <= psxRcntCycles(index))
if (psxCounters[index].target <= psxCounters[index].count)
psxCounters[index].target |= IOPCNT_FUTURE_TARGET;
_rcntSet(index);
@ -803,11 +806,22 @@ void psxRcntWtarget32(int index, u32 value)
// Pulse mode reset
psxCounters[index].mode |= IOPCNT_INT_REQ; // Interrupt flag reset to high
}
if (!(psxCounters[index].mode & IOPCNT_STOPPED) &&
(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;
}
// 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.
if (psxCounters[index].target <= psxRcntCycles(index))
if (psxCounters[index].target <= psxCounters[index].count)
psxCounters[index].target |= IOPCNT_FUTURE_TARGET;
_rcntSet(index);
@ -854,14 +868,6 @@ u32 psxRcntRcount32(int index)
return retval;
}
u64 psxRcntCycles(int index)
{
if (psxCounters[index].mode & IOPCNT_STOPPED || psxCounters[index].rate == PSXHBLANK)
return psxCounters[index].count;
return (u64)(psxCounters[index].count + (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate));
}
void psxRcntSetGates()
{
if (psxCounters[0].mode & IOPCNT_ENABLE_GATE)

View File

@ -44,7 +44,7 @@ static void psxDmaGeneric(u32 madr, u32 bcr, u32 chcr, u32 spuCore)
// Update the spu2 to the current cycle before initiating the DMA
SPU2async(psxRegs.cycle - psxCounters[6].sCycleT);
SPU2async();
//Console.Status("cycles sent to SPU2 %x\n", psxRegs.cycle - psxCounters[6].sCycleT);
psxCounters[6].sCycleT = psxRegs.cycle;

View File

@ -225,7 +225,7 @@ bool SPU2::IsRunningPSXMode()
return s_psxmode;
}
void SPU2async(u32 cycles)
void SPU2async()
{
TimeUpdate(psxRegs.cycle);
}

View File

@ -62,7 +62,7 @@ bool IsAudioCaptureActive();
void SPU2write(u32 mem, u16 value);
u16 SPU2read(u32 mem);
void SPU2async(u32 cycles);
void SPU2async();
s32 SPU2freeze(FreezeAction mode, freezeData* data);
void SPU2readDMA4Mem(u16* pMem, u32 size);