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); 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].count = 0;
counters[index].sCycleT = cpuRegs.cycle; counters[index].sCycleT = cpuRegs.cycle;
} }
@ -81,20 +82,22 @@ void rcntReset(int index) {
// Updates the state of the nextCounter value (if needed) to serve // Updates the state of the nextCounter value (if needed) to serve
// any pending events for the given counter. // any pending events for the given counter.
// Call this method after any modifications to the state of a 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; 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]; const Counter& counter = counters[cntidx];
// Stopped or special hsync gate? // 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 // check for special cases where the overflow or target has just passed
// (we probably missed it because we're doing/checking other things) // (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; nextCounter = 4;
return; return;
@ -106,26 +109,27 @@ static __fi void _rcntSet( int cntidx )
// will do the trick! // will do the trick!
c = ((0x10000 - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT); 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) if (c < nextCounter)
{ {
nextCounter = c; 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. // Ignore target diff if target is currently disabled.
// (the overflow is all we care about since it goes first, and then the // (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). // 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; return;
} }
else else
{ {
c = ((counter.target - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT); 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) if (c < nextCounter)
{ {
nextCounter = c; nextCounter = c;
@ -149,7 +153,7 @@ static __fi void cpuRcntSet()
nextCounter = nextHsync; nextCounter = nextHsync;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
_rcntSet( i ); _rcntSet(i);
// sanity check! // sanity check!
if (nextCounter < 0) if (nextCounter < 0)
@ -166,11 +170,12 @@ void rcntInit()
std::memset(counters, 0, sizeof(counters)); std::memset(counters, 0, sizeof(counters));
for (i=0; i<4; i++) { for (i = 0; i < 4; i++)
{
counters[i].rate = 2; counters[i].rate = 2;
counters[i].target = 0xffff; counters[i].target = 0xffff;
} }
counters[0].interrupt = 9; counters[0].interrupt = 9;
counters[1].interrupt = 10; counters[1].interrupt = 10;
counters[2].interrupt = 11; counters[2].interrupt = 11;
counters[3].interrupt = 12; counters[3].interrupt = 12;
@ -180,7 +185,8 @@ void rcntInit()
vsyncCounter.Mode = MODE_VRENDER; vsyncCounter.Mode = MODE_VRENDER;
vsyncCounter.sCycle = cpuRegs.cycle; vsyncCounter.sCycle = cpuRegs.cycle;
for (i=0; i<4; i++) rcntReset(i); for (i = 0; i < 4; i++)
rcntReset(i);
cpuRcntSet(); cpuRcntSet();
} }
@ -246,11 +252,15 @@ static void vSyncInfoCalc(vSyncTimingInfo* info, double framesPerSecond, u32 sca
info->hBlank = (u32)(hBlank / 10000); info->hBlank = (u32)(hBlank / 10000);
info->hScanlinesPerFrame = scansPerFrame; info->hScanlinesPerFrame = scansPerFrame;
if ((Render % 10000) >= 5000) info->Render++; if ((Render % 10000) >= 5000)
if ((Blank % 10000) >= 5000) info->Blank++; info->Render++;
if ((Blank % 10000) >= 5000)
info->Blank++;
if ((hRender % 10000) >= 5000) info->hRender++; if ((hRender % 10000) >= 5000)
if ((hBlank % 10000) >= 5000) info->hBlank++; info->hRender++;
if ((hBlank % 10000) >= 5000)
info->hBlank++;
// Calculate accumulative hSync rounding error per half-frame: // Calculate accumulative hSync rounding error per half-frame:
if (IsInterlacedVideoMode()) // gets off the chart in that mode 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; info->hSyncError = vSyncCycles - hSyncCycles;
//Console.Warning("%d",info->hSyncError); //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, // 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 // however it would take thousands of frames for it to amount to anything and
// is thus not worth the effort at this time. // is thus not worth the effort at this time.
@ -316,25 +327,25 @@ double GetVerticalFrequency()
switch (gsVideoMode) switch (gsVideoMode)
{ {
case GS_VideoMode::Uninitialized: // SetGsCrt hasn't executed yet, give some temporary values. case GS_VideoMode::Uninitialized: // SetGsCrt hasn't executed yet, give some temporary values.
return 60.00; return 60.00;
case GS_VideoMode::PAL: case GS_VideoMode::PAL:
case GS_VideoMode::DVD_PAL: case GS_VideoMode::DVD_PAL:
return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FrameratePAL : EmuConfig.GS.FrameratePAL - 0.24f; return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FrameratePAL : EmuConfig.GS.FrameratePAL - 0.24f;
case GS_VideoMode::NTSC: case GS_VideoMode::NTSC:
case GS_VideoMode::DVD_NTSC: case GS_VideoMode::DVD_NTSC:
return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FramerateNTSC : EmuConfig.GS.FramerateNTSC - 0.11f; return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FramerateNTSC : EmuConfig.GS.FramerateNTSC - 0.11f;
case GS_VideoMode::SDTV_480P: case GS_VideoMode::SDTV_480P:
return 59.94; return 59.94;
case GS_VideoMode::HDTV_1080P: case GS_VideoMode::HDTV_1080P:
case GS_VideoMode::HDTV_1080I: case GS_VideoMode::HDTV_1080I:
case GS_VideoMode::HDTV_720P: case GS_VideoMode::HDTV_720P:
case GS_VideoMode::SDTV_576P: case GS_VideoMode::SDTV_576P:
case GS_VideoMode::VESA: case GS_VideoMode::VESA:
return 60.00; return 60.00;
default: default:
// Pass NTSC vertical frequency value when unknown video mode is detected. // Pass NTSC vertical frequency value when unknown video mode is detected.
return FRAMERATE_NTSC * 2; 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! gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll) if (EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount ); SysTrace.EE.Counters.Write(" ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount);
hwIntcIrq(INTC_VBLANK_S); hwIntcIrq(INTC_VBLANK_S);
psxVBlankStart(); psxVBlankStart();
if (gates) rcntStartGate(true, sCycle); // Counters Start Gate code if (gates)
rcntStartGate(true, sCycle); // Counters Start Gate code
// INTC - VB Blank Start Hack -- // INTC - VB Blank Start Hack --
// Hack fix! This corrects a freezeup in Granda 2 where it decides to spin // 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) static __fi void VSyncEnd(u32 sCycle)
{ {
if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll) if (EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC END (frame: %d) ================", g_FrameCount ); SysTrace.EE.Counters.Write(" ================ EE COUNTER VSYNC END (frame: %d) ================", g_FrameCount);
g_FrameCount++; g_FrameCount++;
hwIntcIrq(INTC_VBLANK_E); // HW Irq hwIntcIrq(INTC_VBLANK_E); // HW Irq
psxVBlankEnd(); // psxCounters vBlank End 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 // FolderMemoryCard needs information on how much time has passed since the last write
// Call it every 60 frames // 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. //#define VSYNC_DEBUG // Uncomment this to enable some vSync Timer debugging features.
#ifdef VSYNC_DEBUG #ifdef VSYNC_DEBUG
static u32 hsc=0; static u32 hsc = 0;
static int vblankinc = 0; static int vblankinc = 0;
#endif #endif
__fi void rcntUpdate_hScanline() __fi void rcntUpdate_hScanline()
{ {
if( !cpuTestCycle( hsyncCounter.sCycle, hsyncCounter.CycleT ) ) return; if (!cpuTestCycle(hsyncCounter.sCycle, hsyncCounter.CycleT))
return;
//iopEventAction = 1; //iopEventAction = 1;
if (hsyncCounter.Mode == MODE_HBLANK) { //HBLANK Start if (hsyncCounter.Mode == MODE_HBLANK)
{ //HBLANK Start
rcntStartGate(false, hsyncCounter.sCycle); rcntStartGate(false, hsyncCounter.sCycle);
psxCheckStartGate16(0); psxCheckStartGate16(0);
// Setup the hRender's start and end cycle information: // Setup the hRender's start and end cycle information:
hsyncCounter.sCycle += vSyncInfo.hBlank; // start (absolute cycle value) hsyncCounter.sCycle += vSyncInfo.hBlank; // start (absolute cycle value)
hsyncCounter.CycleT = vSyncInfo.hRender; // endpoint (delta from start value) hsyncCounter.CycleT = vSyncInfo.hRender; // endpoint (delta from start value)
hsyncCounter.Mode = MODE_HRENDER; hsyncCounter.Mode = MODE_HRENDER;
} }
else { //HBLANK END / HRENDER Begin else
{ //HBLANK END / HRENDER Begin
if (!CSRreg.HSINT) if (!CSRreg.HSINT)
{ {
CSRreg.HSINT = true; CSRreg.HSINT = true;
if (!GSIMR.HSMSK) if (!GSIMR.HSMSK)
gsIrq(); gsIrq();
} }
if (gates) rcntEndGate(false, hsyncCounter.sCycle); if (gates)
if (psxhblankgate) psxCheckEndGate16(0); rcntEndGate(false, hsyncCounter.sCycle);
if (psxhblankgate)
psxCheckEndGate16(0);
// set up the hblank's start and end cycle information: // set up the hblank's start and end cycle information:
hsyncCounter.sCycle += vSyncInfo.hRender; // start (absolute cycle value) hsyncCounter.sCycle += vSyncInfo.hRender; // start (absolute cycle value)
hsyncCounter.CycleT = vSyncInfo.hBlank; // endpoint (delta from start value) hsyncCounter.CycleT = vSyncInfo.hBlank; // endpoint (delta from start value)
hsyncCounter.Mode = MODE_HBLANK; hsyncCounter.Mode = MODE_HBLANK;
# ifdef VSYNC_DEBUG #ifdef VSYNC_DEBUG
hsc++; hsc++;
# endif #endif
} }
} }
__fi void rcntUpdate_vSync() __fi void rcntUpdate_vSync()
{ {
if (!cpuTestCycle(vsyncCounter.sCycle, vsyncCounter.CycleT)) return; if (!cpuTestCycle(vsyncCounter.sCycle, vsyncCounter.CycleT))
return;
if (vsyncCounter.Mode == MODE_VSYNC) 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 // Don't set the start cycle, makes it easier to calculate the correct Vsync End time
vsyncCounter.CycleT = vSyncInfo.Blank; vsyncCounter.CycleT = vSyncInfo.Blank;
} }
else // VSYNC end / VRENDER begin else // VSYNC end / VRENDER begin
{ {
VSyncStart(vsyncCounter.sCycle); VSyncStart(vsyncCounter.sCycle);
@ -645,25 +664,26 @@ __fi void rcntUpdate_vSync()
// Accumulate hsync rounding errors: // Accumulate hsync rounding errors:
hsyncCounter.sCycle += vSyncInfo.hSyncError; hsyncCounter.sCycle += vSyncInfo.hSyncError;
# ifdef VSYNC_DEBUG #ifdef VSYNC_DEBUG
vblankinc++; vblankinc++;
if( vblankinc > 1 ) if (vblankinc > 1)
{ {
if( hsc != vSyncInfo.hScanlinesPerFrame ) if (hsc != vSyncInfo.hScanlinesPerFrame)
Console.WriteLn( " ** vSync > Abnormal Scanline Count: %d", hsc ); Console.WriteLn(" ** vSync > Abnormal Scanline Count: %d", hsc);
hsc = 0; hsc = 0;
vblankinc = 0; vblankinc = 0;
} }
# endif #endif
} }
} }
static __fi void _cpuTestTarget( int i ) static __fi void _cpuTestTarget(int i)
{ {
if (counters[i].count < counters[i].target) if (counters[i].count < counters[i].target)
return; 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); 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) 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 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); EECNT_LOG("EE Counter[%d] OVERFLOW - mode=%x, count=%x", i, counters[i].mode, counters[i].count);
if (!counters[i].mode.OverflowReached) if (!counters[i].mode.OverflowReached)
{ {
@ -708,57 +730,56 @@ __fi void rcntUpdate()
// Update counters so that we can perform overflow and target tests. // 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 // We want to count gated counters (except the hblank which exclude below, and are
// counted by the hblank timer instead) // counted by the hblank timer instead)
//if ( gates & (1<<i) ) continue; //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; const u32 change = (cpuRegs.cycle - counters[i].sCycleT) / counters[i].rate;
if( change < 0 ) change = 0; // sanity check! counters[i].count += change;
counters[i].sCycleT += change * counters[i].rate;
counters[i].count += change / counters[i].rate;
change -= (change / counters[i].rate) * counters[i].rate;
counters[i].sCycleT = cpuRegs.cycle - change;
// Check Counter Targets and Overflows: // Check Counter Targets and Overflows:
// Check Overflow first, in case the target is 0 // Check Overflow first, in case the target is 0
_cpuTestOverflow( i ); _cpuTestOverflow(i);
_cpuTestTarget(i); _cpuTestTarget(i);
} }
else counters[i].sCycleT = cpuRegs.cycle; else
counters[i].sCycleT = cpuRegs.cycle;
} }
cpuRcntSet(); cpuRcntSet();
} }
static __fi void _rcntSetGate( int index ) static __fi void _rcntSetGate(int index)
{ {
if (counters[index].mode.EnableGate) if (counters[index].mode.EnableGate)
{ {
// If the Gate Source is hblank and the clock selection is also hblank // 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. // 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.", EECNT_LOG("EE Counter[%d] Using Gate! Source=%s, Mode=%d.",
index, counters[index].mode.GateSource ? "vblank" : "hblank", counters[index].mode.GateMode ); index, counters[index].mode.GateSource ? "vblank" : "hblank", counters[index].mode.GateMode);
gates |= (1<<index); gates |= (1 << index);
counters[index].mode.IsCounting = 0; counters[index].mode.IsCounting = 0;
rcntReset(index); rcntReset(index);
return; return;
} }
else 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. // mode - 0 means hblank source, 8 means vblank source.
@ -766,10 +787,10 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
{ {
int i; int i;
for (i=0; i <=3; i++) for (i = 0; i <= 3; i++)
{ {
//if ((mode == 0) && ((counters[i].mode & 0x83) == 0x83)) //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 // 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 // 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; counters[i].count += HBLANK_COUNTER_SPEED;
_cpuTestOverflow(i); _cpuTestOverflow(i);
_cpuTestTarget( i ); _cpuTestTarget(i);
} }
if (!(gates & (1<<i))) continue; if (!(gates & (1 << i)))
if ((!!counters[i].mode.GateSource) != isVblank) continue; 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) case 0x0: //Count When Signal is low (off)
// Just set the start cycle (sCycleT) -- counting will be done as needed // 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].mode.IsCounting = 0;
counters[i].sCycleT = sCycle; counters[i].sCycleT = sCycle;
EECNT_LOG("EE Counter[%d] %s StartGate Type0, count = %x", i, EECNT_LOG("EE Counter[%d] %s StartGate Type0, count = %x", i,
isVblank ? "vblank" : "hblank", counters[i].count ); isVblank ? "vblank" : "hblank", counters[i].count);
break; 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. // this is the vsync start so do nothing.
break; break;
@ -810,7 +834,7 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
counters[i].target &= 0xffff; counters[i].target &= 0xffff;
counters[i].sCycleT = sCycle; counters[i].sCycleT = sCycle;
EECNT_LOG("EE Counter[%d] %s StartGate Type%d, count = %x", i, 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; break;
} }
} }
@ -824,15 +848,19 @@ static __fi void rcntStartGate(bool isVblank, u32 sCycle)
} }
// mode - 0 means hblank signal, 8 means vblank signal. // 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; int i;
for(i=0; i <=3; i++) { //Gates for counters for (i = 0; i <= 3; i++)
if (!(gates & (1<<i))) continue; { //Gates for counters
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) case 0x0: //Count When Signal is low (off)
// Set the count here. Since the timer is being turned off it's // 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; counters[i].sCycleT = cpuRegs.cycle;
EECNT_LOG("EE Counter[%d] %s EndGate Type0, count = %x", i, EECNT_LOG("EE Counter[%d] %s EndGate Type0, count = %x", i,
isVblank ? "vblank" : "hblank", counters[i].count ); isVblank ? "vblank" : "hblank", counters[i].count);
break; 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 // this is the vsync end so do nothing
break; break;
case 0x2: //Reset and start counting on Vsync end case 0x2: //Reset and start counting on Vsync end
case 0x3: //Reset and start counting on Vsync start and 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].target &= 0xffff;
counters[i].sCycleT = sCycle; counters[i].sCycleT = sCycle;
EECNT_LOG("EE Counter[%d] %s EndGate Type%d, count = %x", i, EECNT_LOG("EE Counter[%d] %s EndGate 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; break;
} }
} }
// Note: No need to set counters here. They'll get set when control returns to // Note: No need to set counters here. They'll get set when control returns to
// rcntUpdate, since we're being called from there anyway. // 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) static __fi void rcntWmode(int index, u32 value)
{ {
if(counters[index].mode.IsCounting) { if (counters[index].mode.IsCounting)
if(counters[index].mode.ClockSource != 0x3) { {
if (counters[index].mode.ClockSource != 0x3)
u32 change = cpuRegs.cycle - counters[index].sCycleT; {
if( change > 0 ) const u32 change = (cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate;
{ counters[index].count += change;
counters[index].count += change / counters[index].rate; counters[index].sCycleT += change * counters[index].rate;
change -= (change / counters[index].rate) * counters[index].rate;
counters[index].sCycleT = cpuRegs.cycle - change;
}
} }
} }
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 // 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). // given value. (yes, the bits are cleared when written with '1's).
counters[index].modeval &= ~(value & 0xc00); counters[index].modeval &= ~(value & 0xc00);
counters[index].modeval = (counters[index].modeval & 0xc00) | (value & 0x3ff); 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 switch (counters[index].mode.ClockSource) { //Clock rate divisers *2, they use BUSCLK speed not PS2CLK
case 0: counters[index].rate = 2; break; 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; case 3: counters[index].rate = vSyncInfo.hBlank+vSyncInfo.hRender; break;
} }
_rcntSetGate( index ); _rcntSetGate(index);
_rcntSet( index ); _rcntSet(index);
} }
static __fi void rcntWcount(int index, u32 value) 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; counters[index].count = value & 0xffff;
// reset the target, and make sure we don't get a premature target. // reset the target, and make sure we don't get a premature target.
counters[index].target &= 0xffff; counters[index].target &= 0xffff;
if( counters[index].count > counters[index].target )
if (counters[index].count > counters[index].target)
counters[index].target |= EECNT_FUTURE_TARGET; counters[index].target |= EECNT_FUTURE_TARGET;
// re-calculate the start cycle of the counter based on elapsed time since the last counter update: _rcntSet(index);
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 );
} }
static __fi void rcntWtarget(int index, u32 value) 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 // If the target is behind the current count, set it up so that the counter must
// overflow first before the target fires: // overflow first before the target fires:
if(counters[index].mode.IsCounting) { if (counters[index].mode.IsCounting)
if(counters[index].mode.ClockSource != 0x3) { {
if (counters[index].mode.ClockSource != 0x3)
u32 change = cpuRegs.cycle - counters[index].sCycleT; {
if( change > 0 ) const u32 change = (cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate;
{ counters[index].count += change;
counters[index].count += change / counters[index].rate; counters[index].sCycleT += change * counters[index].rate;
change -= (change / counters[index].rate) * counters[index].rate;
counters[index].sCycleT = cpuRegs.cycle - change;
}
} }
} }
if( counters[index].target <= rcntCycle(index) ) if (counters[index].target <= counters[index].count)
counters[index].target |= EECNT_FUTURE_TARGET; counters[index].target |= EECNT_FUTURE_TARGET;
_rcntSet( index ); _rcntSet(index);
} }
static __fi void rcntWhold(int index, u32 value) static __fi void rcntWhold(int index, u32 value)
@ -982,8 +998,8 @@ __fi u32 rcntRcount(int index)
return ret; return ret;
} }
template< uint page > template <uint page>
__fi u16 rcntRead32( u32 mem ) __fi u16 rcntRead32(u32 mem)
{ {
// Important DevNote: // Important DevNote:
// Yes this uses a u16 return value on purpose! The upper bits 16 of the counter registers // 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); return psHu16(mem);
} }
template< uint page > template <uint page>
__fi bool rcntWrite32( u32 mem, mem32_t& value ) __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 // [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 // 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; return true;
} }
template u16 rcntRead32<0x00>( u32 mem ); template u16 rcntRead32<0x00>(u32 mem);
template u16 rcntRead32<0x01>( u32 mem ); template u16 rcntRead32<0x01>(u32 mem);
template bool rcntWrite32<0x00>( u32 mem, mem32_t& value ); template bool rcntWrite32<0x00>(u32 mem, mem32_t& value);
template bool rcntWrite32<0x01>( u32 mem, mem32_t& value ); template bool rcntWrite32<0x01>(u32 mem, mem32_t& value);
bool SaveStateBase::rcntFreeze() bool SaveStateBase::rcntFreeze()
{ {
Freeze( counters ); Freeze(counters);
Freeze( hsyncCounter ); Freeze(hsyncCounter);
Freeze( vsyncCounter ); Freeze(vsyncCounter);
Freeze( nextCounter ); Freeze(nextCounter);
Freeze( nextsCounter ); Freeze(nextsCounter);
Freeze( vSyncInfo ); Freeze(vSyncInfo);
Freeze( gsVideoMode ); Freeze(gsVideoMode);
Freeze( gsIsInterlaced ); Freeze(gsIsInterlaced);
Freeze( gates ); Freeze(gates);
if( IsLoading() ) if (IsLoading())
cpuRcntSet(); cpuRcntSet();
return IsOkay(); return IsOkay();

View File

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

View File

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

View File

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