2026-01-12 05:18:12 -05:00
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
2024-07-30 13:42:36 +02:00
// SPDX-License-Identifier: GPL-3.0+
2009-09-08 12:08:10 +00:00
2009-02-09 21:15:56 +00:00
// Note on INTC usage: All counters code is always called from inside the context of an
// event test, so instead of using the iopTestIntc we just set the 0x1070 flags directly.
// The EventText function will pick it up.
2022-04-29 10:39:18 +02:00
#include "IopCounters.h"
2022-04-10 11:45:04 -07:00
#include "R3000A.h"
#include "Common.h"
2020-09-24 15:26:34 +02:00
#include "SPU2/spu2.h"
2020-10-03 17:31:30 +02:00
#include "DEV9/DEV9.h"
2020-11-02 15:52:23 +01:00
#include "USB/USB.h"
2022-04-10 11:45:04 -07:00
#include "IopHw.h"
#include "IopDma.h"
#include "CDVD/CDVD.h"
2009-02-09 21:15:56 +00:00
#include <math.h>
2009-03-01 21:49:17 +00:00
/* Config.PsxType == 1: PAL:
VBlank interlaced 50.00 Hz
VBlank non-interlaced 49.76 Hz
2010-04-25 00:31:27 +00:00
HBlank 15.625 KHz
2009-03-01 21:49:17 +00:00
Config.PsxType == 0: NSTC
VBlank interlaced 59.94 Hz
VBlank non-interlaced 59.82 Hz
HBlank 15.73426573 KHz */
// Misc IOP Clocks
2024-04-28 05:49:51 +01:00
// FIXME: this divider is actually 2.73 (36864000 / 13500000), but not sure what uses it, so this'll do, we should maybe change things to float.
#define PSXPIXEL 3
2020-10-01 08:04:10 +02:00
#define PSXSOUNDCLK ((int)(48000))
2009-03-01 21:49:17 +00:00
2009-04-24 01:14:25 +00:00
psxCounter psxCounters [ NUM_COUNTERS ];
2024-05-05 16:46:19 +01:00
s32 psxNextDeltaCounter ;
2026-03-04 07:59:54 +01:00
u64 psxNextStartCounter ;
2024-04-28 05:49:51 +01:00
bool hBlanking = false ;
bool vBlanking = false ;
2009-02-09 21:15:56 +00:00
// flags when the gate is off or counter disabled. (do not count)
2020-10-01 08:04:10 +02:00
#define IOPCNT_STOPPED (0x10000000ul)
2009-02-09 21:15:56 +00:00
// used to disable targets until after an overflow
2020-10-01 08:04:10 +02:00
#define IOPCNT_FUTURE_TARGET (0x1000000000ULL)
2022-05-29 16:03:14 +01:00
#define IOPCNT_MODE_WRITE_MSK 0x63FF
2024-05-05 16:46:19 +01:00
#define IOPCNT_MODE_FLAG_MSK 0x1C00
2009-02-09 21:15:56 +00:00
2024-05-05 16:46:19 +01:00
#define IOPCNT_GATE_CNT_LOW 0 // Counts only when signal is low (V/H RENDER).
#define IOPCNT_GATE_CLR_END 1 // Counts continuously, clears at end of BLANK.
#define IOPCNT_GATE_CNT_HIGH_ZERO_OFF 2 // Starts at beginning of next BLANK, counts only during BLANK, returns zero any other time.
#define IOPCNT_GATE_START_AT_END 3 // Starts at end of next BLANK, continuous count, no clear.
2009-02-09 21:15:56 +00:00
// Use an arbitrary value to flag HBLANK counters.
// These counters will be counted by the hblank gates coming from the EE,
// which ensures they stay 100% in sync with the EE's hblank counters.
#define PSXHBLANK 0x2001
2024-04-28 05:49:51 +01:00
static bool psxRcntCanCount ( int cntidx )
{
2024-05-05 16:46:19 +01:00
if ( psxCounters [ cntidx ]. mode . stopped )
2024-04-28 05:49:51 +01:00
return false ;
2024-05-05 16:46:19 +01:00
if ( ! ( psxCounters [ cntidx ]. mode . gateEnable ))
2024-04-28 05:49:51 +01:00
return true ;
2024-05-05 16:46:19 +01:00
const u32 gateMode = psxCounters [ cntidx ]. mode . gateMode ;
2024-04-28 05:49:51 +01:00
if ( cntidx == 2 || cntidx == 4 || cntidx == 5 )
{
// Gates being enabled on these counters forces it to disable the counter if being on or off depends on a gate being on or off.
return ( gateMode & 1 );
}
const bool blanking = cntidx == 0 ? hBlanking : vBlanking ;
// Stop counting if Gate mode 0 (only count when rendering) and blanking or Gate mode 2 (only count when blanking) when not blanking
2024-05-05 16:46:19 +01:00
if (( gateMode == IOPCNT_GATE_CNT_LOW && blanking == true ) || ( gateMode == IOPCNT_GATE_CNT_HIGH_ZERO_OFF && blanking == false ))
2024-04-28 05:49:51 +01:00
return false ;
// All other cases allow counting.
return true ;
}
2024-05-05 16:46:19 +01:00
static void psxRcntSync ( int cntidx )
{
if (( psxCounters [ cntidx ]. currentIrqMode . repeatInterrupt ) && ! ( psxCounters [ cntidx ]. currentIrqMode . toggleInterrupt ))
{ //Repeat IRQ mode Pulsed, resets a few cycles after the interrupt, this should do.
psxCounters [ cntidx ]. mode . intrEnable = true ;
}
if ( psxRcntCanCount ( cntidx ) && psxCounters [ cntidx ]. rate != PSXHBLANK )
{
2026-08-09 08:26:17 -07:00
// Same underflow hazard as the EE-side rcntSyncCounter: a baseline transiently
// ahead of psxRegs.cycle turned the old u32 `change` into count += 0xFFFFFFFF
// and startCycle += 2^32 - rate (116.5 s of dead counter at IOP clock), a scar
// that then rode along in savestates. Skip; the counter resumes when cycle
// catches up, at most one tick later.
if (( s64 )( psxRegs . cycle - psxCounters [ cntidx ]. startCycle ) < 0 )
2026-08-09 10:01:37 -07:00
{
// Unreachable unless the IOP clock moved backwards — see the EE-side
// note in rcntSyncCounter. Loud on purpose.
Console . Warning ( "psxRcntSync: counter %d baseline ahead of cycle by %lld — IOP clock went backwards?" ,
cntidx , ( long long )( psxCounters [ cntidx ]. startCycle - psxRegs . cycle ));
2026-08-09 08:26:17 -07:00
return ;
2026-08-09 10:01:37 -07:00
}
2026-08-09 08:26:17 -07:00
const u64 change = ( psxRegs . cycle - psxCounters [ cntidx ]. startCycle ) / psxCounters [ cntidx ]. rate ;
2024-05-05 16:46:19 +01:00
if ( change > 0 )
{
psxCounters [ cntidx ]. count += change ;
psxCounters [ cntidx ]. startCycle += change * psxCounters [ cntidx ]. rate ;
2026-03-07 03:24:51 +01:00
psxCounters [ cntidx ]. startCycle &= ~ (( u64 ) psxCounters [ cntidx ]. rate - 1 );
2024-05-05 16:46:19 +01:00
}
}
else
{
if ( psxCounters [ cntidx ]. mode . gateEnable && psxCounters [ cntidx ]. mode . gateMode == IOPCNT_GATE_CNT_HIGH_ZERO_OFF )
psxCounters [ cntidx ]. count = 0 ;
psxCounters [ cntidx ]. startCycle = psxRegs . cycle ;
}
}
2020-10-01 08:04:10 +02:00
static void _rcntSet ( int cntidx )
2009-02-09 21:15:56 +00:00
{
2020-10-01 08:04:10 +02:00
u64 overflowCap = ( cntidx >= 3 ) ? 0x100000000ULL : 0x10000 ;
2009-02-09 21:15:56 +00:00
u64 c ;
const psxCounter & counter = psxCounters [ cntidx ];
// psxNextCounter is relative to the psxRegs.cycle when rcntUpdate() was last called.
// However, the current _rcntSet could be called at any cycle count, so we need to take
// that into account. Adding the difference from that cycle count to the current one
// will do the trick!
2024-04-28 05:49:51 +01:00
if ( counter . rate == PSXHBLANK || ! psxRcntCanCount ( cntidx ))
2020-10-01 08:04:10 +02:00
return ;
2009-02-09 21:15:56 +00:00
// check for special cases where the overflow or target has just passed
// (we probably missed it because we're doing/checking other things)
2020-10-01 08:04:10 +02:00
if ( counter . count > overflowCap || counter . count > counter . target )
2009-02-09 21:15:56 +00:00
{
2024-05-05 16:46:19 +01:00
psxNextDeltaCounter = 4 ;
2009-02-09 21:15:56 +00:00
return ;
}
2026-03-04 07:59:54 +01:00
// FIXME: the u32 casts in this expression exist to match pre-64bit counter code, rewrite these
c = ( u64 )(( overflowCap - counter . count ) * counter . rate ) - (( u32 ) psxRegs . cycle - ( u32 ) counter . startCycle );
2024-05-05 16:46:19 +01:00
c += psxRegs . cycle - psxNextStartCounter ; // adjust for time passed since last rcntUpdate();
2009-05-06 23:51:27 +00:00
2024-05-05 16:46:19 +01:00
if ( c < ( u64 ) psxNextDeltaCounter )
2009-05-06 23:51:27 +00:00
{
2024-05-05 16:46:19 +01:00
psxNextDeltaCounter = ( u32 ) c ;
psxSetNextBranch ( psxNextStartCounter , psxNextDeltaCounter ); //Need to update on counter resets/target changes
2009-05-06 23:51:27 +00:00
}
2009-02-09 21:15:56 +00:00
2020-10-01 08:04:10 +02:00
if ( counter . target & IOPCNT_FUTURE_TARGET )
return ;
2009-02-09 21:15:56 +00:00
2026-03-04 07:59:54 +01:00
c = ( s64 )(( counter . target - counter . count ) * counter . rate ) - (( u32 ) psxRegs . cycle - ( u32 ) counter . startCycle );
2024-05-05 16:46:19 +01:00
c += psxRegs . cycle - psxNextStartCounter ; // adjust for time passed since last rcntUpdate();
2009-05-06 23:51:27 +00:00
2024-05-05 16:46:19 +01:00
if ( c < ( u64 ) psxNextDeltaCounter )
2009-05-06 23:51:27 +00:00
{
2024-05-05 16:46:19 +01:00
psxNextDeltaCounter = ( u32 ) c ;
psxSetNextBranch ( psxNextStartCounter , psxNextDeltaCounter ); //Need to update on counter resets/target changes
2009-05-06 23:51:27 +00:00
}
2009-02-09 21:15:56 +00:00
}
2020-10-01 08:04:10 +02:00
void psxRcntInit ()
{
2009-02-09 21:15:56 +00:00
int i ;
2023-06-16 21:25:52 +10:00
std :: memset ( psxCounters , 0 , sizeof ( psxCounters ));
2009-02-09 21:15:56 +00:00
2020-10-01 08:04:10 +02:00
for ( i = 0 ; i < 3 ; i ++ )
{
2009-02-09 21:15:56 +00:00
psxCounters [ i ]. rate = 1 ;
2024-05-05 16:46:19 +01:00
psxCounters [ i ]. mode . intrEnable = true ;
2009-02-09 21:15:56 +00:00
psxCounters [ i ]. target = IOPCNT_FUTURE_TARGET ;
2024-05-05 16:46:19 +01:00
psxCounters [ i ]. currentIrqMode . repeatInterrupt = false ;
psxCounters [ i ]. currentIrqMode . toggleInterrupt = false ;
2009-02-09 21:15:56 +00:00
}
2020-10-01 08:04:10 +02:00
for ( i = 3 ; i < 6 ; i ++ )
{
2009-02-09 21:15:56 +00:00
psxCounters [ i ]. rate = 1 ;
2024-05-05 16:46:19 +01:00
psxCounters [ i ]. mode . intrEnable = true ;
2009-02-09 21:15:56 +00:00
psxCounters [ i ]. target = IOPCNT_FUTURE_TARGET ;
2024-05-05 16:46:19 +01:00
psxCounters [ i ]. currentIrqMode . repeatInterrupt = false ;
psxCounters [ i ]. currentIrqMode . toggleInterrupt = false ;
2009-02-09 21:15:56 +00:00
}
psxCounters [ 0 ]. interrupt = 0x10 ;
psxCounters [ 1 ]. interrupt = 0x20 ;
psxCounters [ 2 ]. interrupt = 0x40 ;
psxCounters [ 3 ]. interrupt = 0x04000 ;
psxCounters [ 4 ]. interrupt = 0x08000 ;
psxCounters [ 5 ]. interrupt = 0x10000 ;
2023-11-04 03:19:44 +00:00
psxCounters [ 6 ]. rate = 768 ;
2024-05-05 16:46:19 +01:00
psxCounters [ 6 ]. deltaCycles = psxCounters [ 6 ]. rate ;
psxCounters [ 6 ]. mode . modeval = 0x8 ;
2009-02-09 21:15:56 +00:00
2020-11-02 15:52:23 +01:00
psxCounters [ 7 ]. rate = PSXCLK / 1000 ;
2024-05-05 16:46:19 +01:00
psxCounters [ 7 ]. deltaCycles = psxCounters [ 7 ]. rate ;
psxCounters [ 7 ]. mode . modeval = 0x8 ;
2009-02-09 21:15:56 +00:00
2020-10-01 08:04:10 +02:00
for ( i = 0 ; i < 8 ; i ++ )
2024-05-05 16:46:19 +01:00
psxCounters [ i ]. startCycle = psxRegs . cycle ;
2009-02-09 21:15:56 +00:00
// Tell the IOP to branch ASAP, so that timers can get
// configured properly.
2024-05-05 16:46:19 +01:00
psxNextDeltaCounter = 1 ;
psxNextStartCounter = psxRegs . cycle ;
2009-02-09 21:15:56 +00:00
}
2024-05-05 16:46:19 +01:00
static void _rcntFireInterrupt ( int i , bool isOverflow )
2020-10-01 08:04:10 +02:00
{
2024-05-05 16:46:19 +01:00
bool updateIntr = psxCounters [ i ]. currentIrqMode . repeatInterrupt ;
if ( psxCounters [ i ]. mode . intrEnable )
2022-05-29 16:03:14 +01:00
{
2024-05-05 16:46:19 +01:00
bool already_set = isOverflow ? psxCounters [ i ]. mode . overflowFlag : psxCounters [ i ]. mode . targetFlag ;
if ( updateIntr || ! already_set )
2015-10-16 00:48:26 +01:00
{
2024-05-05 16:46:19 +01:00
// IRQ fired
//DevCon.Warning("Counter %d %s IRQ Fired count %x target %x psx Cycle %d", i, isOverflow ? "Overflow" : "Target", psxCounters[i].count, psxCounters[i].target, psxRegs.cycle);
2026-03-05 09:07:57 +01:00
psxHu32 ( HW_ISTAT ) |= psxCounters [ i ]. interrupt ;
2024-05-05 16:46:19 +01:00
iopTestIntc ();
}
updateIntr |= psxCounters [ i ]. currentIrqMode . toggleInterrupt ;
}
if ( updateIntr )
{
if ( psxCounters [ i ]. currentIrqMode . toggleInterrupt )
{
// Toggle mode
psxCounters [ i ]. mode . intrEnable ^= true ; // Interrupt flag inverted
}
else
{
psxCounters [ i ]. mode . intrEnable = false ; // Interrupt flag set low
2015-10-16 00:48:26 +01:00
}
}
}
2024-05-05 16:46:19 +01:00
2022-05-12 22:34:42 +10:00
static void _rcntTestTarget ( int i )
2009-02-09 21:15:56 +00:00
{
2020-10-01 08:04:10 +02:00
if ( psxCounters [ i ]. count < psxCounters [ i ]. target )
return ;
2009-02-09 21:15:56 +00:00
2009-03-27 06:34:51 +00:00
PSXCNT_LOG ( "IOP Counter[%d] target 0x%I64x >= 0x%I64x (mode: %x)" ,
2024-05-05 16:46:19 +01:00
i , psxCounters [ i ]. count , psxCounters [ i ]. target , psxCounters [ i ]. mode . modeval );
2009-02-09 21:15:56 +00:00
2024-05-05 16:46:19 +01:00
if ( psxCounters [ i ]. mode . targetIntr )
2009-02-09 21:15:56 +00:00
{
// Target interrupt
2024-05-05 16:46:19 +01:00
_rcntFireInterrupt ( i , false );
2009-02-09 21:15:56 +00:00
}
2010-04-25 00:31:27 +00:00
2024-05-05 16:46:19 +01:00
psxCounters [ i ]. mode . targetFlag = true ;
if ( psxCounters [ i ]. mode . zeroReturn )
2009-02-09 21:15:56 +00:00
{
// Reset on target
psxCounters [ i ]. count -= psxCounters [ i ]. target ;
2015-10-16 00:48:26 +01:00
}
else
psxCounters [ i ]. target |= IOPCNT_FUTURE_TARGET ;
2009-02-09 21:15:56 +00:00
}
2020-10-01 08:04:10 +02:00
static __fi void _rcntTestOverflow ( int i )
2009-02-09 21:15:56 +00:00
{
2020-10-01 08:04:10 +02:00
u64 maxTarget = ( i < 3 ) ? 0xffff : 0xfffffffful ;
if ( psxCounters [ i ]. count <= maxTarget )
return ;
2009-02-09 21:15:56 +00:00
2009-03-27 06:34:51 +00:00
PSXCNT_LOG ( "IOP Counter[%d] overflow 0x%I64x >= 0x%I64x (mode: %x)" ,
2024-05-05 16:46:19 +01:00
i , psxCounters [ i ]. count , maxTarget , psxCounters [ i ]. mode . modeval );
2022-06-06 14:27:53 +01:00
2024-05-05 16:46:19 +01:00
if ( psxCounters [ i ]. mode . overflIntr )
2015-10-16 00:48:26 +01:00
{
2022-06-06 14:27:53 +01:00
// Overflow interrupt
2024-05-05 16:46:19 +01:00
_rcntFireInterrupt ( i , true );
2009-02-09 21:15:56 +00:00
}
2010-04-25 00:31:27 +00:00
2024-05-05 16:46:19 +01:00
psxCounters [ i ]. mode . overflowFlag = true ;
2015-10-16 00:48:26 +01:00
// Update count.
// Count wraps around back to zero, while the target is restored (if not in one shot mode).
2009-02-09 21:15:56 +00:00
// (high bit of the target gets set by rcntWtarget when the target is behind
// the counter value, and thus should not be flagged until after an overflow)
2023-06-30 18:26:27 +01:00
psxCounters [ i ]. count -= maxTarget + 1 ;
2022-05-29 16:03:14 +01:00
psxCounters [ i ]. target &= maxTarget ;
2009-02-09 21:15:56 +00:00
}
/*
Gate:
TM_NO_GATE 000
TM_GATE_ON_Count 001
TM_GATE_ON_ClearStart 011
TM_GATE_ON_Clear_OFF_Start 101
TM_GATE_ON_Start 111
2024-04-28 05:49:51 +01:00
= means counting
- means not counting
2009-02-09 21:15:56 +00:00
V-blank ----+ +----------------------------+ +------
| | | |
| | | |
+----+ +----+
TM_NO_GATE:
0================================>============
TM_GATE_ON_Count:
2024-04-28 05:49:51 +01:00
<---->===========================><---->======
2009-02-09 21:15:56 +00:00
TM_GATE_ON_ClearStart:
2024-04-28 05:49:51 +01:00
=====>0================================>0=====
2009-02-09 21:15:56 +00:00
TM_GATE_ON_Clear_OFF_Start:
2024-04-28 05:49:51 +01:00
0====>0-------------------------->0====>0-----
2009-02-09 21:15:56 +00:00
TM_GATE_ON_Start:
2024-04-28 05:49:51 +01:00
<---->===========================>============
2009-02-09 21:15:56 +00:00
*/
2020-10-01 08:04:10 +02:00
static void _psxCheckStartGate ( int i )
2009-02-09 21:15:56 +00:00
{
2024-05-05 16:46:19 +01:00
if ( ! ( psxCounters [ i ]. mode . gateEnable ))
2022-05-29 16:03:14 +01:00
return ; // Ignore Gate
2009-02-09 21:15:56 +00:00
2024-05-05 16:46:19 +01:00
switch ( psxCounters [ i ]. mode . gateMode )
2009-02-09 21:15:56 +00:00
{
2024-04-28 05:49:51 +01:00
case 0x0 : // GATE_ON_count - count while gate signal is low (RENDER)
2009-02-09 21:15:56 +00:00
// get the current count at the time of stoppage:
2020-10-01 08:04:10 +02:00
psxCounters [ i ]. count = ( i < 3 ) ?
psxRcntRcount16 ( i ) :
psxRcntRcount32 ( i );
2009-02-09 21:15:56 +00:00
2024-04-28 05:49:51 +01:00
// Not strictly necessary.
2026-03-07 03:24:51 +01:00
psxCounters [ i ]. startCycle = psxRegs . cycle & ~ (( u64 ) psxCounters [ i ]. rate - 1 );
2020-10-01 08:04:10 +02:00
break ;
2009-02-09 21:15:56 +00:00
2024-04-28 05:49:51 +01:00
case 0x1 : // GATE_ON_ClearStart - Counts constantly, clears on Blank END
2023-11-04 03:19:44 +00:00
// do nothing!
2024-04-28 05:49:51 +01:00
break ;
case 0x2 : // GATE_ON_Clear_OFF_Start - Counts only when Blanking, clears on both ends, starts counting on next Blank Start.
2024-05-05 16:46:19 +01:00
psxRcntSync ( i );
psxCounters [ i ]. mode . stopped = false ;
2024-04-28 05:49:51 +01:00
psxCounters [ i ]. count = 0 ;
psxCounters [ i ]. target &= ~ IOPCNT_FUTURE_TARGET ;
break ;
case 0x3 : //GATE_ON_Start - Starts counting when the next Blank Ends, no clear.
// do nothing!
break ;
2009-02-09 21:15:56 +00:00
}
}
static void _psxCheckEndGate ( int i )
{
2024-05-05 16:46:19 +01:00
if ( ! ( psxCounters [ i ]. mode . gateEnable ))
2022-05-29 16:03:14 +01:00
return ; // Ignore Gate
2009-02-09 21:15:56 +00:00
2024-04-28 05:49:51 +01:00
// NOTE: Starting and stopping of modes 0 and 2 are checked in psxRcntCanCount(), only need to update the start cycle and counts.
2024-05-05 16:46:19 +01:00
switch ( psxCounters [ i ]. mode . gateMode )
2009-02-09 21:15:56 +00:00
{
2024-04-28 05:49:51 +01:00
case 0x0 : // GATE_ON_count - count while gate signal is low (RENDER)
2026-03-07 03:24:51 +01:00
psxCounters [ i ]. startCycle = psxRegs . cycle & ~ (( u64 ) psxCounters [ i ]. rate - 1 );
2020-10-01 08:04:10 +02:00
break ;
2009-02-09 21:15:56 +00:00
2024-04-28 05:49:51 +01:00
case 0x1 : // GATE_ON_ClearStart - Counts constantly, clears on Blank END
2024-05-05 16:46:19 +01:00
psxRcntSync ( i );
2024-04-28 05:49:51 +01:00
psxCounters [ i ]. count = 0 ;
psxCounters [ i ]. target &= ~ IOPCNT_FUTURE_TARGET ;
break ;
2009-02-09 21:15:56 +00:00
2024-04-28 05:49:51 +01:00
case 0x2 : // GATE_ON_Clear_OFF_Start - Counts only when Blanking, clears on both ends, starts counting on next Blank Start.
// No point in updating the count, since we're gonna clear it.
2024-05-05 16:46:19 +01:00
psxRcntSync ( i );
2024-04-28 05:49:51 +01:00
psxCounters [ i ]. count = 0 ;
psxCounters [ i ]. target &= ~ IOPCNT_FUTURE_TARGET ;
break ; // do not set the counter
case 0x3 : // GATE_ON_Start - Starts counting when the next Blank Ends, no clear.
2024-05-05 16:46:19 +01:00
if ( psxCounters [ i ]. mode . stopped )
2009-02-09 21:15:56 +00:00
{
2026-03-07 03:24:51 +01:00
psxCounters [ i ]. startCycle = psxRegs . cycle & ~ (( u64 ) psxCounters [ i ]. rate - 1 );
2024-05-05 16:46:19 +01:00
psxCounters [ i ]. mode . stopped = false ;
2009-02-09 21:15:56 +00:00
}
2020-10-01 08:04:10 +02:00
break ;
2009-02-09 21:15:56 +00:00
}
}
2024-04-28 05:49:51 +01:00
void psxHBlankStart ()
2009-02-09 21:15:56 +00:00
{
2024-04-28 05:49:51 +01:00
// AlternateSource/scanline counters for Gates 1 and 3.
// We count them here so that they stay nicely synced with the EE's hsync.
if (( psxCounters [ 1 ]. rate == PSXHBLANK ) && psxRcntCanCount ( 1 ))
2009-02-09 21:15:56 +00:00
{
2024-04-28 05:49:51 +01:00
psxCounters [ 1 ]. count ++ ;
_rcntTestOverflow ( 1 );
_rcntTestTarget ( 1 );
2009-02-09 21:15:56 +00:00
}
2024-04-28 05:49:51 +01:00
if (( psxCounters [ 3 ]. rate == PSXHBLANK ) && psxRcntCanCount ( 3 ))
{
psxCounters [ 3 ]. count ++ ;
_rcntTestOverflow ( 3 );
_rcntTestTarget ( 3 );
}
_psxCheckStartGate ( 0 );
hBlanking = true ;
_rcntSet ( 0 );
2009-02-09 21:15:56 +00:00
}
2024-04-28 05:49:51 +01:00
void psxHBlankEnd ()
2009-02-09 21:15:56 +00:00
{
2024-04-28 05:49:51 +01:00
_psxCheckEndGate ( 0 );
2009-02-09 21:15:56 +00:00
2024-04-28 05:49:51 +01:00
hBlanking = false ;
2009-02-09 21:15:56 +00:00
2024-04-28 05:49:51 +01:00
_rcntSet ( 0 );
2009-02-09 21:15:56 +00:00
}
void psxVBlankStart ()
{
cdvdVsync ();
2018-06-28 18:08:10 +02:00
iopIntcIrq ( 0 );
2024-04-28 05:49:51 +01:00
_psxCheckStartGate ( 1 );
_psxCheckStartGate ( 3 );
vBlanking = true ;
_rcntSet ( 1 );
_rcntSet ( 3 );
2009-02-09 21:15:56 +00:00
}
void psxVBlankEnd ()
{
2018-06-28 18:08:10 +02:00
iopIntcIrq ( 11 );
2024-04-28 05:49:51 +01:00
_psxCheckEndGate ( 1 );
_psxCheckEndGate ( 3 );
vBlanking = false ;
_rcntSet ( 1 );
_rcntSet ( 3 );
2009-02-09 21:15:56 +00:00
}
void psxRcntUpdate ()
{
int i ;
2024-05-05 16:46:19 +01:00
psxNextDeltaCounter = 0x7fffffff ;
psxNextStartCounter = psxRegs . cycle ;
2009-05-06 23:51:27 +00:00
2024-04-28 05:49:51 +01:00
for ( i = 0 ; i < 6 ; i ++ )
2009-02-09 21:15:56 +00:00
{
2009-06-18 04:03:27 +00:00
// don't count disabled or hblank counters...
2009-02-09 21:15:56 +00:00
// We can't check the ALTSOURCE flag because the PSXCLOCK source *should*
// be counted here.
2015-10-16 00:48:26 +01:00
2024-05-05 16:46:19 +01:00
psxRcntSync ( i );
2015-10-16 00:48:26 +01:00
2020-10-01 08:04:10 +02:00
if ( psxCounters [ i ]. rate == PSXHBLANK )
continue ;
2024-05-05 16:46:19 +01:00
if ( ! psxRcntCanCount ( i ))
continue ;
2009-02-09 21:15:56 +00:00
2020-10-01 08:04:10 +02:00
_rcntTestOverflow ( i );
2023-06-30 18:18:47 +01:00
_rcntTestTarget ( i );
2009-02-09 21:15:56 +00:00
}
2010-04-25 00:31:27 +00:00
2023-11-04 03:19:44 +00:00
const u32 spu2_delta = ( psxRegs . cycle - lClocks ) % 768 ;
2024-05-05 16:46:19 +01:00
psxCounters [ 6 ]. startCycle = psxRegs . cycle - spu2_delta ;
psxCounters [ 6 ]. deltaCycles = psxCounters [ 6 ]. rate ;
2023-11-04 03:19:44 +00:00
SPU2async ();
2024-05-05 16:46:19 +01:00
psxNextDeltaCounter = psxCounters [ 6 ]. deltaCycles ;
2023-11-04 03:19:44 +00:00
2024-05-05 16:46:19 +01:00
DEV9async ( 1 );
const s32 diffusb = psxRegs . cycle - psxCounters [ 7 ]. startCycle ;
s32 cusb = psxCounters [ 7 ]. deltaCycles ;
2009-02-09 21:15:56 +00:00
2024-05-05 16:46:19 +01:00
if ( diffusb >= psxCounters [ 7 ]. deltaCycles )
2022-05-29 16:03:14 +01:00
{
2020-11-02 15:52:23 +01:00
USBasync ( diffusb );
2024-05-05 16:46:19 +01:00
psxCounters [ 7 ]. startCycle += psxCounters [ 7 ]. rate * ( diffusb / psxCounters [ 7 ]. rate );
psxCounters [ 7 ]. deltaCycles = psxCounters [ 7 ]. rate ;
2022-05-29 16:03:14 +01:00
}
else
2020-11-02 15:52:23 +01:00
cusb -= diffusb ;
2022-05-29 16:03:14 +01:00
2024-05-05 16:46:19 +01:00
if ( cusb < psxNextDeltaCounter )
psxNextDeltaCounter = cusb ;
2009-02-09 21:15:56 +00:00
2020-10-01 08:04:10 +02:00
for ( i = 0 ; i < 6 ; i ++ )
_rcntSet ( i );
2009-02-09 21:15:56 +00:00
}
2009-05-02 10:48:41 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
//
void psxRcntWcount16 ( int index , u16 value )
2009-02-09 21:15:56 +00:00
{
2020-10-01 08:04:10 +02:00
pxAssert ( index < 3 );
2024-05-05 16:46:19 +01:00
PSXCNT_LOG ( "16bit IOP Counter[%d] writeCount16 = %x" , index , value );
psxRcntSync ( index );
2009-02-09 21:15:56 +00:00
psxCounters [ index ]. count = value & 0xffff ;
2022-05-29 16:03:14 +01:00
2023-11-04 03:19:44 +00:00
psxCounters [ index ]. target &= 0xffff ;
2022-05-29 16:03:14 +01:00
if ( psxCounters [ index ]. count > psxCounters [ index ]. target )
2020-10-01 08:04:10 +02:00
{
2022-05-29 16:03:14 +01:00
// Count already higher than Target
//DevCon.Warning("32bit Count already higher than target");
2015-10-16 00:48:26 +01:00
psxCounters [ index ]. target |= IOPCNT_FUTURE_TARGET ;
}
2022-05-29 16:03:14 +01:00
2020-10-01 08:04:10 +02:00
_rcntSet ( index );
2009-02-09 21:15:56 +00:00
}
2009-05-02 10:48:41 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
//
2009-02-09 21:15:56 +00:00
void psxRcntWcount32 ( int index , u32 value )
{
2020-10-01 08:04:10 +02:00
pxAssert ( index >= 3 && index < 6 );
2015-10-16 00:48:26 +01:00
PSXCNT_LOG ( "32bit IOP Counter[%d] writeCount32 = %x" , index , value );
2010-04-25 00:31:27 +00:00
2024-05-05 16:46:19 +01:00
psxRcntSync ( index );
2009-02-09 21:15:56 +00:00
2015-10-16 00:48:26 +01:00
psxCounters [ index ]. count = value ;
2022-09-13 21:20:25 -05:00
2023-11-04 03:19:44 +00:00
psxCounters [ index ]. target &= 0xffffffff ;
2022-05-29 16:03:14 +01:00
if ( psxCounters [ index ]. count > psxCounters [ index ]. target )
2022-09-13 21:20:25 -05:00
{
2022-05-29 16:03:14 +01:00
// Count already higher than Target
2015-10-16 00:48:26 +01:00
//DevCon.Warning("32bit Count already higher than target");
psxCounters [ index ]. target |= IOPCNT_FUTURE_TARGET ;
}
2022-05-29 16:03:14 +01:00
2020-10-01 08:04:10 +02:00
_rcntSet ( index );
2009-02-09 21:15:56 +00:00
}
2009-05-02 10:48:41 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
//
2015-10-16 00:48:26 +01:00
__fi void psxRcntWmode16 ( int index , u32 value )
2009-02-09 21:15:56 +00:00
{
2015-10-16 00:48:26 +01:00
int irqmode = 0 ;
PSXCNT_LOG ( "16bit IOP Counter[%d] writeMode = 0x%04X" , index , value );
2009-02-09 21:15:56 +00:00
2015-10-16 00:48:26 +01:00
pxAssume ( index >= 0 && index < 3 );
2009-05-02 10:48:41 +00:00
psxCounter & counter = psxCounters [ index ];
2024-05-05 16:46:19 +01:00
psxCounterMode oldMode = counter . mode ;
2009-02-09 21:15:56 +00:00
2024-05-05 16:46:19 +01:00
counter . mode . modeval = ( value & IOPCNT_MODE_WRITE_MSK ) | ( counter . mode . modeval & IOPCNT_MODE_FLAG_MSK ); // Write new value, preserve flags
2009-05-02 10:48:41 +00:00
2024-05-05 16:46:19 +01:00
if ( ! (( oldMode . targetFlag || oldMode . overflowFlag ) && ( oldMode . targetIntr || oldMode . overflIntr )))
psxRcntSetNewIntrMode ( index );
if ( counter . mode . repeatIntr != counter . currentIrqMode . repeatInterrupt || counter . mode . toggleIntr != counter . currentIrqMode . toggleInterrupt )
DevCon . Warning ( "Write to psxCounter[%d] mode old repeat %d new %d old toggle %d new %d" , index , counter . currentIrqMode . repeatInterrupt , counter . mode . repeatIntr , counter . currentIrqMode . toggleInterrupt , counter . mode . toggleIntr );
2020-10-01 08:04:10 +02:00
if ( value & ( 1 << 4 ))
{
2015-10-16 00:48:26 +01:00
irqmode += 1 ;
}
2020-10-01 08:04:10 +02:00
if ( value & ( 1 << 5 ))
{
2015-10-16 00:48:26 +01:00
irqmode += 2 ;
}
2020-10-01 08:04:10 +02:00
if ( value & ( 1 << 7 ))
{
2015-10-16 00:48:26 +01:00
PSXCNT_LOG ( "16 Counter %d Toggle IRQ on %s" , index , ( irqmode & 3 ) == 1 ? "Target" : (( irqmode & 3 ) == 2 ? "Overflow" : "Target and Overflow" ));
}
else
{
PSXCNT_LOG ( "16 Counter %d Pulsed IRQ on %s" , index , ( irqmode & 3 ) == 1 ? "Target" : (( irqmode & 3 ) == 2 ? "Overflow" : "Target and Overflow" ));
}
2020-10-01 08:04:10 +02:00
if ( ! ( value & ( 1 << 6 )))
{
2015-10-16 00:48:26 +01:00
PSXCNT_LOG ( "16 Counter %d One Shot" , index );
}
2020-10-01 08:04:10 +02:00
else
{
2015-10-16 00:48:26 +01:00
PSXCNT_LOG ( "16 Counter %d Repeat" , index );
}
2020-10-01 08:04:10 +02:00
if ( index == 2 )
2009-02-09 21:15:56 +00:00
{
2024-05-05 16:46:19 +01:00
if ( counter . mode . t2Prescale )
psxCounters [ 2 ]. rate = 8 ;
else
psxCounters [ 2 ]. rate = 1 ;
2009-05-02 10:48:41 +00:00
}
else
{
// Counters 0 and 1 can select PIXEL or HSYNC as an alternate source:
counter . rate = 1 ;
2024-05-05 16:46:19 +01:00
if ( counter . mode . extSignal )
2020-10-01 08:04:10 +02:00
counter . rate = ( index == 0 ) ? PSXPIXEL : PSXHBLANK ;
2009-05-02 10:48:41 +00:00
2024-04-28 05:49:51 +01:00
if ( counter . rate == PSXPIXEL )
Console . Warning ( "PSX Pixel clock set to time 0, sync may be incorrect" );
2024-05-05 16:46:19 +01:00
if ( counter . mode . gateEnable )
2009-05-02 10:48:41 +00:00
{
2024-05-05 16:46:19 +01:00
// If set to gate mode 2 or 3, the counting starts at the start and end of the next blank respectively depending on which counter.
if ( counter . mode . gateMode >= IOPCNT_GATE_CNT_HIGH_ZERO_OFF )
counter . mode . stopped = true ;
2024-04-28 05:49:51 +01:00
2020-10-01 08:04:10 +02:00
PSXCNT_LOG ( "IOP Counter[%d] Gate Check set, value = 0x%04X" , index , value );
2009-05-02 10:48:41 +00:00
}
2009-02-09 21:15:56 +00:00
}
2024-04-28 05:49:51 +01:00
// Current counter *always* resets on mode write.
2009-05-02 10:48:41 +00:00
counter . count = 0 ;
2026-03-07 03:24:51 +01:00
counter . startCycle = psxRegs . cycle & ~ (( u64 ) counter . rate - 1 );
2015-10-16 00:48:26 +01:00
counter . target &= 0xffff ;
2020-10-01 08:04:10 +02:00
_rcntSet ( index );
2009-02-09 21:15:56 +00:00
}
2009-05-02 10:48:41 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
//
2020-10-01 08:04:10 +02:00
__fi void psxRcntWmode32 ( int index , u32 value )
2009-02-09 21:15:56 +00:00
{
2020-10-01 08:04:10 +02:00
PSXCNT_LOG ( "32bit IOP Counter[%d] writeMode = 0x%04x" , index , value );
2015-10-16 00:48:26 +01:00
int irqmode = 0 ;
2020-10-01 08:04:10 +02:00
pxAssume ( index >= 3 && index < 6 );
2009-05-02 10:48:41 +00:00
psxCounter & counter = psxCounters [ index ];
2024-05-05 16:46:19 +01:00
psxCounterMode oldMode = counter . mode ;
2009-02-09 21:15:56 +00:00
2024-05-05 16:46:19 +01:00
counter . mode . modeval = ( value & IOPCNT_MODE_WRITE_MSK ) | ( counter . mode . modeval & IOPCNT_MODE_FLAG_MSK ); // Write new value, preserve flags
2009-05-02 10:48:41 +00:00
2024-05-05 16:46:19 +01:00
if ( ! (( oldMode . targetFlag || oldMode . overflowFlag ) && ( oldMode . targetIntr || oldMode . overflIntr )))
psxRcntSetNewIntrMode ( index );
if ( counter . mode . repeatIntr != counter . currentIrqMode . repeatInterrupt || counter . mode . toggleIntr != counter . currentIrqMode . toggleInterrupt )
DevCon . Warning ( "Write to psxCounter[%d] mode old repeat %d new %d old toggle %d new %d" , index , counter . currentIrqMode . repeatInterrupt , counter . mode . repeatIntr , counter . currentIrqMode . toggleInterrupt , counter . mode . toggleIntr );
2020-10-01 08:04:10 +02:00
if ( value & ( 1 << 4 ))
{
2015-10-16 00:48:26 +01:00
irqmode += 1 ;
}
2020-10-01 08:04:10 +02:00
if ( value & ( 1 << 5 ))
{
2015-10-16 00:48:26 +01:00
irqmode += 2 ;
}
2020-10-01 08:04:10 +02:00
if ( value & ( 1 << 7 ))
{
2015-10-16 00:48:26 +01:00
PSXCNT_LOG ( "32 Counter %d Toggle IRQ on %s" , index , ( irqmode & 3 ) == 1 ? "Target" : (( irqmode & 3 ) == 2 ? "Overflow" : "Target and Overflow" ));
}
else
{
PSXCNT_LOG ( "32 Counter %d Pulsed IRQ on %s" , index , ( irqmode & 3 ) == 1 ? "Target" : (( irqmode & 3 ) == 2 ? "Overflow" : "Target and Overflow" ));
}
2020-10-01 08:04:10 +02:00
if ( ! ( value & ( 1 << 6 )))
2009-02-09 21:15:56 +00:00
{
2020-10-01 08:04:10 +02:00
PSXCNT_LOG ( "32 Counter %d One Shot" , index );
2009-02-09 21:15:56 +00:00
}
2009-05-02 10:48:41 +00:00
else
2009-02-09 21:15:56 +00:00
{
2020-10-01 08:04:10 +02:00
PSXCNT_LOG ( "32 Counter %d Repeat" , index );
}
if ( index == 3 )
{
// Counter 3 has the HBlank as an alternate source.
counter . rate = 1 ;
2024-05-05 16:46:19 +01:00
if ( counter . mode . extSignal )
2020-10-01 08:04:10 +02:00
counter . rate = PSXHBLANK ;
2024-05-05 16:46:19 +01:00
if ( counter . mode . gateEnable )
2009-05-02 10:48:41 +00:00
{
2020-10-01 08:04:10 +02:00
PSXCNT_LOG ( "IOP Counter[3] Gate Check set, value = %x" , value );
2024-04-28 05:49:51 +01:00
// If set to gate mode 2 or 3, the counting starts at the start and end of the next blank respectively depending on which counter.
2024-05-05 16:46:19 +01:00
if ( counter . mode . gateMode >= IOPCNT_GATE_CNT_HIGH_ZERO_OFF )
counter . mode . stopped = true ;
2020-10-01 08:04:10 +02:00
}
}
else
{
2024-05-05 16:46:19 +01:00
switch ( counter . mode . t4_5Prescale )
2020-10-01 08:04:10 +02:00
{
2024-05-05 16:46:19 +01:00
case 0x1 :
2020-10-01 08:04:10 +02:00
counter . rate = 8 ;
break ;
2024-05-05 16:46:19 +01:00
case 0x2 :
2020-10-01 08:04:10 +02:00
counter . rate = 16 ;
break ;
2024-05-05 16:46:19 +01:00
case 0x3 :
2020-10-01 08:04:10 +02:00
counter . rate = 256 ;
break ;
2024-05-05 16:46:19 +01:00
default :
counter . rate = 1 ;
break ;
2009-05-02 10:48:41 +00:00
}
2009-02-09 21:15:56 +00:00
}
2009-05-02 10:48:41 +00:00
2024-04-28 05:49:51 +01:00
// Current counter *always* resets on mode write.
2009-05-02 10:48:41 +00:00
counter . count = 0 ;
2026-05-08 13:16:14 +01:00
counter . startCycle = psxRegs . cycle & ~ ( static_cast < u64 > ( counter . rate - 1 ));
2009-05-02 10:48:41 +00:00
counter . target &= 0xffffffff ;
2020-10-01 08:04:10 +02:00
_rcntSet ( index );
2009-02-09 21:15:56 +00:00
}
2009-05-02 10:48:41 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
//
2009-02-09 21:15:56 +00:00
void psxRcntWtarget16 ( int index , u32 value )
{
2020-10-01 08:04:10 +02:00
pxAssert ( index < 3 );
2022-06-06 14:27:53 +01:00
PSXCNT_LOG ( "IOP Counter[%d] writeTarget16 = %lx" , index , value );
2009-02-09 21:15:56 +00:00
psxCounters [ index ]. target = value & 0xffff ;
2024-05-05 16:46:19 +01:00
psxRcntSync ( index );
2023-11-04 03:19:44 +00:00
2009-02-09 21:15:56 +00:00
// 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.
2010-04-25 00:31:27 +00:00
2023-11-04 03:19:44 +00:00
if ( psxCounters [ index ]. target <= psxCounters [ index ]. count )
2009-02-09 21:15:56 +00:00
psxCounters [ index ]. target |= IOPCNT_FUTURE_TARGET ;
2020-10-01 08:04:10 +02:00
_rcntSet ( index );
2009-02-09 21:15:56 +00:00
}
void psxRcntWtarget32 ( int index , u32 value )
{
2020-10-01 08:04:10 +02:00
pxAssert ( index >= 3 && index < 6 );
2024-05-05 16:46:19 +01:00
PSXCNT_LOG ( "IOP Counter[%d] writeTarget32 = %lx mode %x" , index , value , psxCounters [ index ]. mode . modeval );
2009-02-09 21:15:56 +00:00
psxCounters [ index ]. target = value ;
2022-05-29 16:03:14 +01:00
2024-05-05 16:46:19 +01:00
psxRcntSync ( index );
2009-02-09 21:15:56 +00:00
// 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.
2023-11-04 03:19:44 +00:00
if ( psxCounters [ index ]. target <= psxCounters [ index ]. count )
2009-02-09 21:15:56 +00:00
psxCounters [ index ]. target |= IOPCNT_FUTURE_TARGET ;
2020-10-01 08:04:10 +02:00
_rcntSet ( index );
2009-02-09 21:15:56 +00:00
}
u16 psxRcntRcount16 ( int index )
{
2024-05-05 16:46:19 +01:00
psxRcntSync ( index );
const u32 retval = ( u32 ) psxCounters [ index ]. count ;
2009-02-09 21:15:56 +00:00
2020-10-01 08:04:10 +02:00
pxAssert ( index < 3 );
2009-02-09 21:15:56 +00:00
2020-10-01 08:04:10 +02:00
PSXCNT_LOG ( "IOP Counter[%d] readCount16 = %lx" , index , ( u16 ) retval );
2009-02-09 21:15:56 +00:00
return ( u16 ) retval ;
}
u32 psxRcntRcount32 ( int index )
{
2024-05-05 16:46:19 +01:00
psxRcntSync ( index );
const u32 retval = ( u32 ) psxCounters [ index ]. count ;
2010-04-25 00:31:27 +00:00
2020-10-01 08:04:10 +02:00
pxAssert ( index >= 3 && index < 6 );
2009-02-09 21:15:56 +00:00
2020-10-01 08:04:10 +02:00
PSXCNT_LOG ( "IOP Counter[%d] readCount32 = %lx" , index , retval );
2009-02-09 21:15:56 +00:00
return retval ;
}
2024-05-05 16:46:19 +01:00
void psxRcntSetNewIntrMode ( int index )
{
psxCounters [ index ]. mode . targetFlag = false ;
psxCounters [ index ]. mode . overflowFlag = false ;
psxCounters [ index ]. mode . intrEnable = true ;
2026-02-06 22:00:06 +00:00
//if (psxCounters[index].mode.repeatIntr != psxCounters[index].currentIrqMode.repeatInterrupt || psxCounters[index].mode.toggleIntr != psxCounters[index].currentIrqMode.toggleInterrupt)
// DevCon.Warning("Updating psxCounter[%d] mode old repeat %d new %d old toggle %d new %d", index, psxCounters[index].mode.repeatIntr, psxCounters[index].currentIrqMode.repeatInterrupt, psxCounters[index].mode.toggleIntr, psxCounters[index].currentIrqMode.toggleInterrupt);
2024-05-05 16:46:19 +01:00
psxCounters [ index ]. currentIrqMode . repeatInterrupt = psxCounters [ index ]. mode . repeatIntr ;
psxCounters [ index ]. currentIrqMode . toggleInterrupt = psxCounters [ index ]. mode . toggleIntr ;
}
2022-12-28 21:13:38 +10:00
bool SaveStateBase :: psxRcntFreeze ()
2009-02-09 21:15:56 +00:00
{
2022-12-28 21:13:38 +10:00
if ( ! FreezeTag ( "iopCounters" ))
return false ;
2009-03-19 04:16:24 +00:00
2020-10-01 08:04:10 +02:00
Freeze ( psxCounters );
2024-05-05 16:46:19 +01:00
Freeze ( psxNextDeltaCounter );
Freeze ( psxNextStartCounter );
2024-04-28 05:49:51 +01:00
Freeze ( hBlanking );
Freeze ( vBlanking );
2010-04-25 00:31:27 +00:00
2022-12-28 21:13:38 +10:00
if ( ! IsOkay ())
return false ;
2020-10-01 08:04:10 +02:00
if ( IsLoading ())
2026-08-09 08:26:17 -07:00
{
2026-08-09 10:03:57 -07:00
// DELETEME after 2026-12-01: transitional repair for old poisoned states.
2026-08-09 08:26:17 -07:00
// Repair states poisoned by the old u32 psxRcntSync blowup (baseline one full
// 2^32 epoch in the future): snap the baseline back to now, or the counter
// stays dead until psxRegs.cycle crosses the bogus baseline (116.5 s). The
// count is left alone — IOP counts are u64 and several counters legitimately
2026-08-09 10:03:57 -07:00
// run wider than 16 bits. Same removal terms as the EE-side block in
// rcntFreeze: the trigger was fixed 2026-08-09, this only heals states
// saved by older builds; delete the loop once those have aged out.
2026-08-09 08:26:17 -07:00
for ( int i = 0 ; i < NUM_COUNTERS ; i ++ )
{
if (( s64 )( psxRegs . cycle - psxCounters [ i ]. startCycle ) >= 0 )
continue ;
Console . Warning ( "psxRcntFreeze: counter %d baseline %llu is ahead of cycle %llu; repairing poisoned state" ,
i , psxCounters [ i ]. startCycle , psxRegs . cycle );
psxCounters [ i ]. startCycle = psxRegs . cycle & ~ (( u64 ) psxCounters [ i ]. rate - 1 );
}
2021-10-20 10:41:50 +01:00
psxRcntUpdate ();
2026-08-09 08:26:17 -07:00
}
2022-12-28 21:13:38 +10:00
return true ;
2009-02-09 21:15:56 +00:00
}