cdrom: change pause timing again
[pcsx_rearmed.git] / libpcsxcore / psxcounters.c
index 5056c70..887fe8a 100644 (file)
  */
 
 #include "psxcounters.h"
+#include "psxevents.h"
 #include "gpu.h"
-#include "debug.h"
+//#include "debug.h"
+#define DebugVSync()
 
 /******************************************************************************/
 
 enum
 {
-    Rc0Gate           = 0x0001, // 0    not implemented
-    Rc1Gate           = 0x0001, // 0    not implemented
-    Rc2Disable        = 0x0001, // 0    partially implemented
-    RcUnknown1        = 0x0002, // 1    ?
-    RcUnknown2        = 0x0004, // 2    ?
+    RcSyncModeEnable  = 0x0001, // 0
+    Rc01BlankPause    = 0 << 1, // 1,2
+    Rc01UnblankReset  = 1 << 1, // 1,2
+    Rc01UnblankReset2 = 2 << 1, // 1,2
+    Rc2Stop           = 0 << 1, // 1,2
+    Rc2Stop2          = 3 << 1, // 1,2
     RcCountToTarget   = 0x0008, // 3
     RcIrqOnTarget     = 0x0010, // 4
     RcIrqOnOverflow   = 0x0020, // 5
@@ -59,28 +62,63 @@ enum
 static const u32 CountToOverflow  = 0;
 static const u32 CountToTarget    = 1;
 
-static const u32 FrameRate[]      = { 60, 50 };
-static const u32 HSyncTotal[]     = { 263, 313 };
-static const u32 SpuUpdInterval[] = { 32, 32 };
-#define VBlankStart 240
+static const u32 HSyncTotal[]     = { 263, 314 };
+#define VBlankStart 240 // todo: depend on the actual GPU setting
 
 #define VERBOSE_LEVEL 0
-static const s32 VerboseLevel     = VERBOSE_LEVEL;
 
 /******************************************************************************/
-
+#ifdef DRC_DISABLE
 Rcnt rcnts[ CounterQuantity ];
-
+#endif
 u32 hSyncCount = 0;
 u32 frame_counter = 0;
-static u32 spuSyncCount = 0;
 static u32 hsync_steps = 0;
-static u32 base_cycle = 0;
 
 u32 psxNextCounter = 0, psxNextsCounter = 0;
 
 /******************************************************************************/
 
+#define FPS_FRACTIONAL_PAL  (53203425/314./3406) // ~49.75
+#define FPS_FRACTIONAL_NTSC (53693175/263./3413) // ~59.81
+
+static inline
+u32 frameCycles(void)
+{
+    int ff = Config.FractionalFramerate >= 0
+        ? Config.FractionalFramerate : Config.hacks.fractional_Framerate;
+    if (ff)
+    {
+        if (Config.PsxType)
+            return (u32)(PSXCLK / FPS_FRACTIONAL_PAL);
+        else
+            return (u32)(PSXCLK / FPS_FRACTIONAL_NTSC);
+    }
+    return Config.PsxType ? (PSXCLK / 50) : (PSXCLK / 60);
+}
+
+// used to inform the frontend about the exact framerate
+double psxGetFps()
+{
+    int ff = Config.FractionalFramerate >= 0
+        ? Config.FractionalFramerate : Config.hacks.fractional_Framerate;
+    if (ff)
+        return Config.PsxType ? FPS_FRACTIONAL_PAL : FPS_FRACTIONAL_NTSC;
+    else
+        return Config.PsxType ? 50.0 : 60.0;
+}
+
+// to inform the frontend about the exact famerate
+static inline
+u32 lineCycles(void)
+{
+    // should be more like above, but our timing is already poor anyway
+    if (Config.PsxType)
+        return PSXCLK / 50 / HSyncTotal[1];
+    else
+        return PSXCLK / 60 / HSyncTotal[0];
+}
+
 static inline
 void setIrq( u32 irq )
 {
@@ -91,7 +129,7 @@ static
 void verboseLog( u32 level, const char *str, ... )
 {
 #if VERBOSE_LEVEL > 0
-    if( level <= VerboseLevel )
+    if( level <= VERBOSE_LEVEL )
     {
         va_list va;
         char buf[ 4096 ];
@@ -111,11 +149,7 @@ void verboseLog( u32 level, const char *str, ... )
 static inline
 void _psxRcntWcount( u32 index, u32 value )
 {
-    if( value > 0xffff )
-    {
-        verboseLog( 1, "[RCNT %i] wcount > 0xffff: %x\n", index, value );
-        value &= 0xffff;
-    }
+    value &= 0xffff;
 
     rcnts[index].cycleStart  = psxRegs.cycle;
     rcnts[index].cycleStart -= value * rcnts[index].rate;
@@ -128,7 +162,7 @@ void _psxRcntWcount( u32 index, u32 value )
     }
     else
     {
-        rcnts[index].cycle = 0xffff * rcnts[index].rate;
+        rcnts[index].cycle = 0x10000 * rcnts[index].rate;
         rcnts[index].counterState = CountToOverflow;
     }
 }
@@ -143,15 +177,62 @@ u32 _psxRcntRcount( u32 index )
     if (rcnts[index].rate > 1)
         count /= rcnts[index].rate;
 
-    if( count > 0xffff )
+    if( count > 0x10000 )
     {
-        verboseLog( 1, "[RCNT %i] rcount > 0xffff: %x\n", index, count );
-        count &= 0xffff;
+        verboseLog( 1, "[RCNT %i] rcount > 0x10000: %x\n", index, count );
     }
+    count &= 0xffff;
 
     return count;
 }
 
+static
+void _psxRcntWmode( u32 index, u32 value )
+{
+    rcnts[index].mode = value;
+
+    switch( index )
+    {
+        case 0:
+            if( value & Rc0PixelClock )
+            {
+                rcnts[index].rate = 5;
+            }
+            else
+            {
+                rcnts[index].rate = 1;
+            }
+        break;
+        case 1:
+            if( value & Rc1HSyncClock )
+            {
+                rcnts[index].rate = lineCycles();
+            }
+            else
+            {
+                rcnts[index].rate = 1;
+            }
+        break;
+        case 2:
+            if( value & Rc2OneEighthClock )
+            {
+                rcnts[index].rate = 8;
+            }
+            else
+            {
+                rcnts[index].rate = 1;
+            }
+
+            // TODO: wcount must work.
+            if( (value & 7) == (RcSyncModeEnable | Rc2Stop) ||
+                (value & 7) == (RcSyncModeEnable | Rc2Stop2) )
+            {
+                rcnts[index].rate = 0xffffffff;
+            }
+        break;
+    }
+}
+
 /******************************************************************************/
 
 static
@@ -179,8 +260,7 @@ void psxRcntSet()
         }
     }
 
-    psxRegs.interrupt |= (1 << PSXINT_RCNT);
-    new_dyna_set_event(PSXINT_RCNT, psxNextCounter);
+    set_event(PSXINT_RCNT, psxNextCounter);
 }
 
 /******************************************************************************/
@@ -188,26 +268,29 @@ void psxRcntSet()
 static
 void psxRcntReset( u32 index )
 {
-    u32 count;
+    u32 rcycles;
 
     rcnts[index].mode |= RcUnknown10;
 
     if( rcnts[index].counterState == CountToTarget )
     {
-        count  = psxRegs.cycle;
-        count -= rcnts[index].cycleStart;
-        if( rcnts[index].rate > 1 )
-            count /= rcnts[index].rate;
+        rcycles = psxRegs.cycle - rcnts[index].cycleStart;
         if( rcnts[index].mode & RcCountToTarget )
-            count -= rcnts[index].target;
-
-        _psxRcntWcount( index, count );
+        {
+            rcycles -= rcnts[index].target * rcnts[index].rate;
+            rcnts[index].cycleStart = psxRegs.cycle - rcycles;
+        }
+        else
+        {
+            rcnts[index].cycle = 0x10000 * rcnts[index].rate;
+            rcnts[index].counterState = CountToOverflow;
+        }
 
         if( rcnts[index].mode & RcIrqOnTarget )
         {
             if( (rcnts[index].mode & RcIrqRegenerate) || (!rcnts[index].irqState) )
             {
-                verboseLog( 3, "[RCNT %i] irq: %x\n", index, count );
+                verboseLog( 3, "[RCNT %i] irq\n", index );
                 setIrq( rcnts[index].irq );
                 rcnts[index].irqState = 1;
             }
@@ -215,25 +298,28 @@ void psxRcntReset( u32 index )
 
         rcnts[index].mode |= RcCountEqTarget;
 
-        if( count < 0xffff ) // special case, overflow too?
+        if( rcycles < 0x10000 * rcnts[index].rate )
             return;
     }
 
     if( rcnts[index].counterState == CountToOverflow )
     {
-        count  = psxRegs.cycle;
-        count -= rcnts[index].cycleStart;
-        if (rcnts[index].rate > 1)
-            count /= rcnts[index].rate;
-        count -= 0xffff;
+        rcycles = psxRegs.cycle - rcnts[index].cycleStart;
+        rcycles -= 0x10000 * rcnts[index].rate;
+
+        rcnts[index].cycleStart = psxRegs.cycle - rcycles;
 
-        _psxRcntWcount( index, count );
+        if( rcycles < rcnts[index].target * rcnts[index].rate )
+        {
+            rcnts[index].cycle = rcnts[index].target * rcnts[index].rate;
+            rcnts[index].counterState = CountToTarget;
+        }
 
         if( rcnts[index].mode & RcIrqOnOverflow )
         {
             if( (rcnts[index].mode & RcIrqRegenerate) || (!rcnts[index].irqState) )
             {
-                verboseLog( 3, "[RCNT %i] irq: %x\n", index, count );
+                verboseLog( 3, "[RCNT %i] irq\n", index );
                 setIrq( rcnts[index].irq );
                 rcnts[index].irqState = 1;
             }
@@ -243,26 +329,59 @@ void psxRcntReset( u32 index )
     }
 }
 
+static void scheduleRcntBase(void)
+{
+    // Schedule next call, in hsyncs
+    if (hSyncCount < VBlankStart)
+        hsync_steps = VBlankStart - hSyncCount;
+    else
+        hsync_steps = HSyncTotal[Config.PsxType] - hSyncCount;
+
+    if (hSyncCount + hsync_steps == HSyncTotal[Config.PsxType])
+    {
+        rcnts[3].cycle = frameCycles();
+    }
+    else
+    {
+        // clk / 50 / 314 ~= 2157.25
+        // clk / 60 / 263 ~= 2146.31
+        u32 mult = Config.PsxType ? 8836089 : 8791293;
+        rcnts[3].cycle = hsync_steps * mult >> 12;
+    }
+}
+
 void psxRcntUpdate()
 {
-    u32 cycle;
+    u32 cycle, cycles_passed;
 
     cycle = psxRegs.cycle;
 
     // rcnt 0.
-    if( cycle - rcnts[0].cycleStart >= rcnts[0].cycle )
+    cycles_passed = cycle - rcnts[0].cycleStart;
+    while( cycles_passed >= rcnts[0].cycle )
     {
-        psxRcntReset( 0 );
+        if (((rcnts[0].mode & 7) == (RcSyncModeEnable | Rc01UnblankReset) ||
+             (rcnts[0].mode & 7) == (RcSyncModeEnable | Rc01UnblankReset2))
+            && cycles_passed > lineCycles())
+        {
+            u32 q = cycles_passed / (lineCycles() + 1u);
+            rcnts[0].cycleStart += q * lineCycles();
+            break;
+        }
+        else
+            psxRcntReset( 0 );
+
+        cycles_passed = cycle - rcnts[0].cycleStart;
     }
 
     // rcnt 1.
-    if( cycle - rcnts[1].cycleStart >= rcnts[1].cycle )
+    while( cycle - rcnts[1].cycleStart >= rcnts[1].cycle )
     {
         psxRcntReset( 1 );
     }
 
     // rcnt 2.
-    if( cycle - rcnts[2].cycleStart >= rcnts[2].cycle )
+    while( cycle - rcnts[2].cycleStart >= rcnts[2].cycle )
     {
         psxRcntReset( 2 );
     }
@@ -270,68 +389,68 @@ void psxRcntUpdate()
     // rcnt base.
     if( cycle - rcnts[3].cycleStart >= rcnts[3].cycle )
     {
-        u32 leftover_cycles = cycle - rcnts[3].cycleStart - rcnts[3].cycle;
-        u32 next_vsync, next_lace;
-
-        spuSyncCount += hsync_steps;
         hSyncCount += hsync_steps;
 
-        // Update spu.
-        if( spuSyncCount >= SpuUpdInterval[Config.PsxType] )
-        {
-            spuSyncCount = 0;
-
-            if( SPU_async )
-            {
-                SPU_async( SpuUpdInterval[Config.PsxType] * rcnts[3].target );
-            }
-        }
-        
         // VSync irq.
         if( hSyncCount == VBlankStart )
         {
-            HW_GPU_STATUS &= ~PSXGPU_LCF;
-
+            HW_GPU_STATUS &= SWAP32(~PSXGPU_LCF);
+            GPU_vBlank( 1, 0 );
             setIrq( 0x01 );
 
             EmuUpdate();
             GPU_updateLace();
+
+            if( SPU_async )
+            {
+                SPU_async( cycle, 1 );
+            }
         }
         
-        // Update lace. (with InuYasha fix)
-        if( hSyncCount >= (Config.VSyncWA ? HSyncTotal[Config.PsxType] / BIAS : HSyncTotal[Config.PsxType]) )
+        // Update lace.
+        if( hSyncCount >= HSyncTotal[Config.PsxType] )
         {
+            u32 status, field = 0;
+            rcnts[3].cycleStart += frameCycles();
             hSyncCount = 0;
             frame_counter++;
 
             gpuSyncPluginSR();
-            if( (HW_GPU_STATUS & PSXGPU_ILACE_BITS) == PSXGPU_ILACE_BITS )
-                HW_GPU_STATUS |= frame_counter << 31;
-        }
+            status = SWAP32(HW_GPU_STATUS) | PSXGPU_FIELD;
+            if ((status & PSXGPU_ILACE_BITS) == PSXGPU_ILACE_BITS) {
+                field = frame_counter & 1;
+                status |= field << 31;
+                status ^= field << 13;
+            }
+            HW_GPU_STATUS = SWAP32(status);
+            GPU_vBlank(0, field);
+            if ((s32)(psxRegs.gpuIdleAfter - psxRegs.cycle) < 0)
+                psxRegs.gpuIdleAfter = psxRegs.cycle - 1; // prevent overflow
+
+            if ((rcnts[0].mode & 7) == (RcSyncModeEnable | Rc01UnblankReset) ||
+                (rcnts[0].mode & 7) == (RcSyncModeEnable | Rc01UnblankReset2))
+            {
+                rcnts[0].cycleStart = rcnts[3].cycleStart;
+            }
 
-        // Schedule next call, in hsyncs
-        hsync_steps = SpuUpdInterval[Config.PsxType] - spuSyncCount;
-        next_vsync = VBlankStart - hSyncCount; // ok to overflow
-        next_lace = HSyncTotal[Config.PsxType] - hSyncCount;
-        if( next_vsync && next_vsync < hsync_steps )
-            hsync_steps = next_vsync;
-        if( next_lace && next_lace < hsync_steps )
-            hsync_steps = next_lace;
+            if ((rcnts[1].mode & 7) == (RcSyncModeEnable | Rc01UnblankReset) ||
+                (rcnts[1].mode & 7) == (RcSyncModeEnable | Rc01UnblankReset2))
+            {
+                rcnts[1].cycleStart = rcnts[3].cycleStart;
+            }
+            else if (rcnts[1].mode & Rc1HSyncClock)
+            {
+                // adjust to remove the rounding error
+                _psxRcntWcount(1, (psxRegs.cycle - rcnts[1].cycleStart) / rcnts[1].rate);
+            }
+        }
 
-        rcnts[3].cycleStart = cycle - leftover_cycles;
-        if (Config.PsxType)
-                // 20.12 precision, clk / 50 / 313 ~= 2164.14
-                base_cycle += hsync_steps * 8864320;
-        else
-                // clk / 60 / 263 ~= 2146.31
-                base_cycle += hsync_steps * 8791293;
-        rcnts[3].cycle = base_cycle >> 12;
-        base_cycle &= 0xfff;
+        scheduleRcntBase();
     }
 
     psxRcntSet();
 
-#ifndef NDEBUG
+#if 0 //ndef NDEBUG
     DebugVSync();
 #endif
 }
@@ -350,50 +469,10 @@ void psxRcntWmode( u32 index, u32 value )
 {
     verboseLog( 1, "[RCNT %i] wmode: %x\n", index, value );
 
-    rcnts[index].mode = value;
-    rcnts[index].irqState = 0;
-
-    switch( index )
-    {
-        case 0:
-            if( value & Rc0PixelClock )
-            {
-                rcnts[index].rate = 5;
-            }
-            else
-            {
-                rcnts[index].rate = 1;
-            }
-        break;
-        case 1:
-            if( value & Rc1HSyncClock )
-            {
-                rcnts[index].rate = (PSXCLK / (FrameRate[Config.PsxType] * HSyncTotal[Config.PsxType]));
-            }
-            else
-            {
-                rcnts[index].rate = 1;
-            }
-        break;
-        case 2:
-            if( value & Rc2OneEighthClock )
-            {
-                rcnts[index].rate = 8;
-            }
-            else
-            {
-                rcnts[index].rate = 1;
-            }
-
-            // TODO: wcount must work.
-            if( value & Rc2Disable )
-            {
-                rcnts[index].rate = 0xffffffff;
-            }
-        break;
-    }
-
+    _psxRcntWmode( index, value );
     _psxRcntWcount( index, 0 );
+
+    rcnts[index].irqState = 0;
     psxRcntSet();
 }
 
@@ -409,25 +488,47 @@ void psxRcntWtarget( u32 index, u32 value )
 
 /******************************************************************************/
 
-u32 psxRcntRcount( u32 index )
+u32 psxRcntRcount0()
 {
+    u32 index = 0;
     u32 count;
 
-    count = _psxRcntRcount( index );
-
-    // Parasite Eve 2 fix.
-    if( Config.RCntFix )
+    if ((rcnts[0].mode & 7) == (RcSyncModeEnable | Rc01UnblankReset) ||
+        (rcnts[0].mode & 7) == (RcSyncModeEnable | Rc01UnblankReset2))
     {
-        if( index == 2 )
-        {
-            if( rcnts[index].counterState == CountToTarget )
-            {
-                count /= BIAS;
-            }
-        }
+        count = psxRegs.cycle - rcnts[index].cycleStart;
+        //count = ((16u * count) % (16u * PSXCLK / 60 / 263)) / 16u;
+        count = count % lineCycles();
+        rcnts[index].cycleStart = psxRegs.cycle - count;
     }
+    else
+        count = _psxRcntRcount( index );
+
+    verboseLog( 2, "[RCNT 0] rcount: %04x m: %04x\n", count, rcnts[index].mode);
+
+    return count;
+}
+
+u32 psxRcntRcount1()
+{
+    u32 index = 1;
+    u32 count;
+
+    count = _psxRcntRcount( index );
+
+    verboseLog( 2, "[RCNT 1] rcount: %04x m: %04x\n", count, rcnts[index].mode);
+
+    return count;
+}
+
+u32 psxRcntRcount2()
+{
+    u32 index = 2;
+    u32 count;
+
+    count = _psxRcntRcount( index );
 
-    verboseLog( 2, "[RCNT %i] rcount: %x\n", index, count );
+    verboseLog( 2, "[RCNT 2] rcount: %04x m: %04x\n", count, rcnts[index].mode);
 
     return count;
 }
@@ -471,8 +572,6 @@ void psxRcntInit()
 
     // rcnt base.
     rcnts[3].rate   = 1;
-    rcnts[3].mode   = RcCountToTarget;
-    rcnts[3].target = (PSXCLK / (FrameRate[Config.PsxType] * HSyncTotal[Config.PsxType]));
 
     for( i = 0; i < CounterQuantity; ++i )
     {
@@ -480,28 +579,42 @@ void psxRcntInit()
     }
 
     hSyncCount = 0;
-    spuSyncCount = 0;
     hsync_steps = 1;
 
+    scheduleRcntBase();
     psxRcntSet();
 }
 
 /******************************************************************************/
 
-s32 psxRcntFreeze( gzFile f, s32 Mode )
+s32 psxRcntFreeze( void *f, s32 Mode )
 {
-    gzfreeze( &rcnts, sizeof(rcnts) );
+    u32 spuSyncCount = 0;
+    u32 count;
+    s32 i;
+
+    gzfreeze( &rcnts, sizeof(Rcnt) * CounterQuantity );
     gzfreeze( &hSyncCount, sizeof(hSyncCount) );
     gzfreeze( &spuSyncCount, sizeof(spuSyncCount) );
     gzfreeze( &psxNextCounter, sizeof(psxNextCounter) );
     gzfreeze( &psxNextsCounter, sizeof(psxNextsCounter) );
 
     if (Mode == 0)
-        hsync_steps = (psxRegs.cycle - rcnts[3].cycleStart) / rcnts[3].target;
-
-    base_cycle = 0;
+    {
+        rcnts[3].rate = 1;
+        for( i = 0; i < CounterQuantity - 1; ++i )
+        {
+            _psxRcntWmode( i, rcnts[i].mode );
+            count = (psxRegs.cycle - rcnts[i].cycleStart) / rcnts[i].rate;
+            if (count > 0x1000)
+                _psxRcntWcount( i, count & 0xffff );
+        }
+        scheduleRcntBase();
+        psxRcntSet();
+    }
 
     return 0;
 }
 
 /******************************************************************************/
+// vim:ts=4:shiftwidth=4:expandtab