mirror of https://github.com/PCSX2/pcsx2.git
Counters: Improve counter updates and fix a couple of errors
This commit is contained in:
parent
65d4baa944
commit
b824c2d31a
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,14 +508,15 @@ static __fi void VSyncStart(u32 sCycle)
|
|||
VMManager::Internal::Throttle();
|
||||
|
||||
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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -225,7 +225,7 @@ bool SPU2::IsRunningPSXMode()
|
|||
return s_psxmode;
|
||||
}
|
||||
|
||||
void SPU2async(u32 cycles)
|
||||
void SPU2async()
|
||||
{
|
||||
TimeUpdate(psxRegs.cycle);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue