/[pcsx2_0.9.7]/trunk/pcsx2/Counters.cpp
ViewVC logotype

Annotation of /trunk/pcsx2/Counters.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (hide annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 5 months ago) by william
File size: 25601 byte(s)
committing r3113 initial commit again...
1 william 31 /* PCSX2 - PS2 Emulator for PCs
2     * Copyright (C) 2002-2010 PCSX2 Dev Team
3     *
4     * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5     * of the GNU Lesser General Public License as published by the Free Software Found-
6     * ation, either version 3 of the License, or (at your option) any later version.
7     *
8     * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10     * PURPOSE. See the GNU General Public License for more details.
11     *
12     * You should have received a copy of the GNU General Public License along with PCSX2.
13     * If not, see <http://www.gnu.org/licenses/>.
14     */
15    
16    
17     #include "PrecompiledHeader.h"
18     #include "Common.h"
19    
20     #include <time.h>
21     #include <cmath>
22    
23     #include "R3000A.h"
24     #include "Counters.h"
25     #include "IopCounters.h"
26    
27     #include "GS.h"
28     #include "VUmicro.h"
29    
30     using namespace Threading;
31    
32     extern u8 psxhblankgate;
33    
34     static const uint EECNT_FUTURE_TARGET = 0x10000000;
35     static int gates = 0;
36    
37     uint g_FrameCount = 0;
38    
39     // Counter 4 takes care of scanlines - hSync/hBlanks
40     // Counter 5 takes care of vSync/vBlanks
41     Counter counters[4];
42     SyncCounter hsyncCounter;
43     SyncCounter vsyncCounter;
44    
45     u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate()
46     s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate()
47    
48    
49     void rcntReset(int index) {
50     counters[index].count = 0;
51     counters[index].sCycleT = cpuRegs.cycle;
52     }
53    
54     // Updates the state of the nextCounter value (if needed) to serve
55     // any pending events for the given counter.
56     // Call this method after any modifications to the state of a counter.
57     static __forceinline void _rcntSet( int cntidx )
58     {
59     s32 c;
60     jASSUME( cntidx <= 4 ); // rcntSet isn't valid for h/vsync counters.
61    
62     const Counter& counter = counters[cntidx];
63    
64     // Stopped or special hsync gate?
65     if (!counter.mode.IsCounting || (counter.mode.ClockSource == 0x3) ) return;
66    
67     // check for special cases where the overflow or target has just passed
68     // (we probably missed it because we're doing/checking other things)
69     if( counter.count > 0x10000 || counter.count > counter.target )
70     {
71     nextCounter = 4;
72     return;
73     }
74    
75     // nextCounter is relative to the cpuRegs.cycle when rcntUpdate() was last called.
76     // However, the current _rcntSet could be called at any cycle count, so we need to take
77     // that into account. Adding the difference from that cycle count to the current one
78     // will do the trick!
79    
80     c = ((0x10000 - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
81     c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
82     if (c < nextCounter)
83     {
84     nextCounter = c;
85     cpuSetNextBranch( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
86     }
87    
88     // Ignore target diff if target is currently disabled.
89     // (the overflow is all we care about since it goes first, and then the
90     // target will be turned on afterward, and handled in the next event test).
91    
92     if( counter.target & EECNT_FUTURE_TARGET )
93     {
94     return;
95     }
96     else
97     {
98     c = ((counter.target - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
99     c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
100     if (c < nextCounter)
101     {
102     nextCounter = c;
103     cpuSetNextBranch( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
104     }
105     }
106     }
107    
108    
109     static __forceinline void cpuRcntSet()
110     {
111     int i;
112    
113     nextsCounter = cpuRegs.cycle;
114     nextCounter = vsyncCounter.CycleT - (cpuRegs.cycle - vsyncCounter.sCycle);
115    
116     for (i = 0; i < 4; i++)
117     _rcntSet( i );
118    
119     // sanity check!
120     if( nextCounter < 0 ) nextCounter = 0;
121     }
122    
123     void rcntInit()
124     {
125     int i;
126    
127     g_FrameCount = 0;
128    
129     memzero(counters);
130    
131     for (i=0; i<4; i++) {
132     counters[i].rate = 2;
133     counters[i].target = 0xffff;
134     }
135     counters[0].interrupt = 9;
136     counters[1].interrupt = 10;
137     counters[2].interrupt = 11;
138     counters[3].interrupt = 12;
139    
140     hsyncCounter.Mode = MODE_HRENDER;
141     hsyncCounter.sCycle = cpuRegs.cycle;
142     vsyncCounter.Mode = MODE_VRENDER;
143     vsyncCounter.sCycle = cpuRegs.cycle;
144    
145     // Set the video mode to user's default request:
146     gsSetRegionMode( (GS_RegionMode)EmuConfig.GS.DefaultRegionMode );
147    
148     for (i=0; i<4; i++) rcntReset(i);
149     cpuRcntSet();
150     }
151    
152    
153     #ifndef _WIN32
154     #include <sys/time.h>
155     #endif
156    
157     static s64 m_iTicks=0;
158     static u64 m_iStart=0;
159    
160     struct vSyncTimingInfo
161     {
162     Fixed100 Framerate; // frames per second (8 bit fixed)
163     u32 Render; // time from vblank end to vblank start (cycles)
164     u32 Blank; // time from vblank start to vblank end (cycles)
165    
166     u32 hSyncError; // rounding error after the duration of a rendered frame (cycles)
167     u32 hRender; // time from hblank end to hblank start (cycles)
168     u32 hBlank; // time from hblank start to hblank end (cycles)
169     u32 hScanlinesPerFrame; // number of scanlines per frame (525/625 for NTSC/PAL)
170     };
171    
172    
173     static vSyncTimingInfo vSyncInfo;
174    
175    
176     static void vSyncInfoCalc( vSyncTimingInfo* info, Fixed100 framesPerSecond, u32 scansPerFrame )
177     {
178     // I use fixed point math here to have strict control over rounding errors. --air
179    
180     // NOTE: mgs3 likes a /4 vsync, but many games prefer /2. This seems to indicate a
181     // problem in the counters vsync gates somewhere.
182    
183     u64 Frame = ((u64)PS2CLK * 1000000ULL) / (framesPerSecond*100).ToIntRounded();
184     u64 HalfFrame = Frame / 2;
185     u64 Blank = HalfFrame / 2; // two blanks and renders per frame
186     u64 Render = HalfFrame - Blank; // so use the half-frame value for these...
187    
188     // Important! The hRender/hBlank timers should be 50/50 for best results.
189     // (this appears to be what the real EE's timing crystal does anyway)
190    
191     u64 Scanline = Frame / scansPerFrame;
192     u64 hBlank = Scanline / 2;
193     u64 hRender = Scanline - hBlank;
194    
195     info->Framerate = framesPerSecond;
196     info->Render = (u32)(Render/10000);
197     info->Blank = (u32)(Blank/10000);
198    
199     info->hRender = (u32)(hRender/10000);
200     info->hBlank = (u32)(hBlank/10000);
201     info->hScanlinesPerFrame = scansPerFrame;
202    
203     // Apply rounding:
204     if( ( Render - info->Render ) >= 5000 ) info->Render++;
205     else if( ( Blank - info->Blank ) >= 5000 ) info->Blank++;
206    
207     if( ( hRender - info->hRender ) >= 5000 ) info->hRender++;
208     else if( ( hBlank - info->hBlank ) >= 5000 ) info->hBlank++;
209    
210     // Calculate accumulative hSync rounding error per half-frame:
211     {
212     u32 hSyncCycles = ((info->hRender + info->hBlank) * scansPerFrame) / 2;
213     u32 vSyncCycles = (info->Render + info->Blank);
214     info->hSyncError = vSyncCycles - hSyncCycles;
215     }
216    
217     // Note: In NTSC modes there is some small rounding error in the vsync too,
218     // however it would take thousands of frames for it to amount to anything and
219     // is thus not worth the effort at this time.
220     }
221    
222    
223     u32 UpdateVSyncRate()
224     {
225     Registers::Freeze();
226    
227     // Notice: (and I probably repeat this elsewhere, but it's worth repeating)
228     // The PS2's vsync timer is an *independent* crystal that is fixed to either 59.94 (NTSC)
229     // or 50.0 (PAL) Hz. It has *nothing* to do with real TV timings or the real vsync of
230     // the GS's output circuit. It is the same regardless if the GS is outputting interlace
231     // or progressive scan content. Indications are that it is also a simple 50/50 timer and
232     // that it does not actually measure Vblank/Vdraw zones accurately (which would be like
233     // 1/5 and 4/5 ratios).
234    
235     Fixed100 framerate;
236     u32 scanlines;
237     bool isCustom;
238    
239     if( gsRegionMode == Region_PAL )
240     {
241     isCustom = (EmuConfig.GS.FrameratePAL != 50.0);
242     framerate = EmuConfig.GS.FrameratePAL / 2;
243     scanlines = SCANLINES_TOTAL_PAL;
244     }
245     else
246     {
247     isCustom = (EmuConfig.GS.FramerateNTSC != 59.94);
248     framerate = EmuConfig.GS.FramerateNTSC / 2;
249     scanlines = SCANLINES_TOTAL_NTSC;
250     }
251    
252     if( vSyncInfo.Framerate != framerate )
253     {
254     vSyncInfoCalc( &vSyncInfo, framerate, scanlines );
255     Console.WriteLn( Color_Blue, "(UpdateVSyncRate) Mode Changed to %s.", ( gsRegionMode == Region_PAL ) ? "PAL" : "NTSC" );
256     if( isCustom )
257     Console.Indent().WriteLn( Color_StrongBlue, "... with user configured refresh rate: %.02f Hz", framerate.ToFloat() );
258    
259     hsyncCounter.CycleT = vSyncInfo.hRender; // Amount of cycles before the counter will be updated
260     vsyncCounter.CycleT = vSyncInfo.Render; // Amount of cycles before the counter will be updated
261    
262     cpuRcntSet();
263     }
264    
265     Fixed100 fpslimit = framerate *
266     ( pxAssert( EmuConfig.GS.LimitScalar > 0 ) ? EmuConfig.GS.LimitScalar : 1.0 );
267    
268     //s64 debugme = GetTickFrequency() / 3000;
269     s64 ticks = (GetTickFrequency()*500) / (fpslimit * 1000).ToIntRounded();
270    
271     if( m_iTicks != ticks )
272     {
273     m_iTicks = ticks;
274     gsOnModeChanged( vSyncInfo.Framerate, m_iTicks );
275     Console.WriteLn( "(UpdateVSyncRate) FPS Limit Changed : %.02f fps", fpslimit.ToFloat()*2 );
276     }
277    
278     m_iStart = GetCPUTicks();
279    
280     Registers::Thaw();
281    
282     return (u32)m_iTicks;
283     }
284    
285     void frameLimitReset()
286     {
287     m_iStart = GetCPUTicks();
288     }
289    
290     // Framelimiter - Measures the delta time between calls and stalls until a
291     // certain amount of time passes if such time hasn't passed yet.
292     // See the GS FrameSkip function for details on why this is here and not in the GS.
293     static __forceinline void frameLimit()
294     {
295     // 999 means the user would rather just have framelimiting turned off...
296     if( !EmuConfig.GS.FrameLimitEnable ) return;
297    
298     u64 uExpectedEnd = m_iStart + m_iTicks;
299     u64 iEnd = GetCPUTicks();
300     s64 sDeltaTime = iEnd - uExpectedEnd;
301    
302     // If the framerate drops too low, reset the expected value. This avoids
303     // excessive amounts of "fast forward" syndrome which would occur if we
304     // tried to catch up too much.
305    
306     if( sDeltaTime > m_iTicks*8 )
307     {
308     m_iStart = iEnd - m_iTicks;
309     return;
310     }
311    
312     // use the expected frame completion time as our starting point.
313     // improves smoothness by making the framelimiter more adaptive to the
314     // imperfect TIMESLICE() wait, and allows it to speed up a wee bit after
315     // slow frames to "catch up."
316    
317     m_iStart = uExpectedEnd;
318    
319     // Shortcut for cases where no waiting is needed (they're running slow already,
320     // so don't bog 'em down with extra math...)
321     if( sDeltaTime >= 0 ) return;
322    
323     // If we're way ahead then we can afford to sleep the thread a bit.
324     // (note, on Windows sleep(1) thru sleep(2) tend to be the least accurate sleeps,
325     // and longer sleeps tend to be pretty reliable, so that's why the convoluted if/
326     // else below. The same generally isn't true for Linux, but no harm either way
327     // really.)
328    
329     s32 msec = (int)((sDeltaTime*-1000) / (s64)GetTickFrequency());
330     if( msec > 4 ) Threading::Sleep( msec );
331     else if( msec > 2 ) Threading::Sleep( 1 );
332    
333     // Sleep is not picture-perfect accurate, but it's actually not necessary to
334     // maintain a "perfect" lock to uExpectedEnd anyway. if we're a little ahead
335     // starting this frame, it'll just sleep longer the next to make up for it. :)
336     }
337    
338     static __forceinline void VSyncStart(u32 sCycle)
339     {
340     Cpu->CheckExecutionState();
341     GetCoreThread().VsyncInThread();
342    
343     EECNT_LOG( "///////// EE COUNTER VSYNC START (frame: %6d) \\\\\\\\\\\\\\\\\\\\ ", g_FrameCount );
344    
345     // EE Profiling and Debug code.
346     // FIXME: should probably be moved to VsyncInThread, and handled
347     // by UI implementations. (ie, AppCoreThread in PCSX2-wx interface).
348     vSyncDebugStuff( g_FrameCount );
349    
350     CpuVU0->Vsync();
351     CpuVU1->Vsync();
352    
353     if ((CSRw & 0x8))
354     {
355     if (!(GSIMR&0x800))
356     {
357     gsIrq();
358     }
359     GSCSRr|= 0x8;
360     }
361    
362     hwIntcIrq(INTC_VBLANK_S);
363     psxVBlankStart();
364    
365     if (gates) rcntStartGate(true, sCycle); // Counters Start Gate code
366    
367     // INTC - VB Blank Start Hack --
368     // Hack fix! This corrects a freezeup in Granda 2 where it decides to spin
369     // on the INTC_STAT register after the exception handler has already cleared
370     // it. But be warned! Set the value to larger than 4 and it breaks Dark
371     // Cloud and other games. -_-
372    
373     // How it works: Normally the INTC raises exceptions immediately at the end of the
374     // current branch test. But in the case of Grandia 2, the game's code is spinning
375     // on the INTC status, and the exception handler (for some reason?) clears the INTC
376     // before returning *and* returns to a location other than EPC. So the game never
377     // gets to the point where it sees the INTC Irq set true.
378    
379     // (I haven't investigated why Dark Cloud freezes on larger values)
380     // (all testing done using the recompiler -- dunno how the ints respond yet)
381    
382     //cpuRegs.eCycle[30] = 2;
383    
384     // Should no longer be required (Refraction)
385     }
386    
387     static __forceinline void VSyncEnd(u32 sCycle)
388     {
389     EECNT_LOG( "///////// EE COUNTER VSYNC END (frame: %d) \\\\\\\\\\\\\\\\\\\\", g_FrameCount );
390    
391     g_FrameCount++;
392    
393     gsPostVsyncEnd();
394    
395     hwIntcIrq(INTC_VBLANK_E); // HW Irq
396     psxVBlankEnd(); // psxCounters vBlank End
397     if (gates) rcntEndGate(true, sCycle); // Counters End Gate Code
398     frameLimit(); // limit FPS
399    
400     // This doesn't seem to be needed here. Games only seem to break with regard to the
401     // vsyncstart irq.
402     //cpuRegs.eCycle[30] = 2;
403     }
404    
405     //#define VSYNC_DEBUG // Uncomment this to enable some vSync Timer debugging features.
406     #ifdef VSYNC_DEBUG
407     static u32 hsc=0;
408     static int vblankinc = 0;
409     #endif
410    
411     __forceinline void rcntUpdate_hScanline()
412     {
413     if( !cpuTestCycle( hsyncCounter.sCycle, hsyncCounter.CycleT ) ) return;
414    
415     //iopBranchAction = 1;
416     if (hsyncCounter.Mode & MODE_HBLANK) { //HBLANK Start
417     rcntStartGate(false, hsyncCounter.sCycle);
418     psxCheckStartGate16(0);
419    
420     // Setup the hRender's start and end cycle information:
421     hsyncCounter.sCycle += vSyncInfo.hBlank; // start (absolute cycle value)
422     hsyncCounter.CycleT = vSyncInfo.hRender; // endpoint (delta from start value)
423     hsyncCounter.Mode = MODE_HRENDER;
424     }
425     else { //HBLANK END / HRENDER Begin
426     if (CSRw & 0x4)
427     {
428     if (!(GSIMR&0x400))
429     {
430     gsIrq();
431     }
432     GSCSRr |= 4; // signal
433     }
434     if (gates) rcntEndGate(false, hsyncCounter.sCycle);
435     if (psxhblankgate) psxCheckEndGate16(0);
436    
437     // set up the hblank's start and end cycle information:
438     hsyncCounter.sCycle += vSyncInfo.hRender; // start (absolute cycle value)
439     hsyncCounter.CycleT = vSyncInfo.hBlank; // endpoint (delta from start value)
440     hsyncCounter.Mode = MODE_HBLANK;
441    
442     # ifdef VSYNC_DEBUG
443     hsc++;
444     # endif
445     }
446     }
447    
448     __forceinline void rcntUpdate_vSync()
449     {
450     s32 diff = (cpuRegs.cycle - vsyncCounter.sCycle);
451     if( diff < vsyncCounter.CycleT ) return;
452    
453     if (vsyncCounter.Mode == MODE_VSYNC)
454     {
455     VSyncEnd(vsyncCounter.sCycle);
456    
457     vsyncCounter.sCycle += vSyncInfo.Blank;
458     vsyncCounter.CycleT = vSyncInfo.Render;
459     vsyncCounter.Mode = MODE_VRENDER;
460     }
461     else // VSYNC end / VRENDER begin
462     {
463     VSyncStart(vsyncCounter.sCycle);
464    
465     vsyncCounter.sCycle += vSyncInfo.Render;
466     vsyncCounter.CycleT = vSyncInfo.Blank;
467     vsyncCounter.Mode = MODE_VSYNC;
468    
469     // Accumulate hsync rounding errors:
470     hsyncCounter.sCycle += vSyncInfo.hSyncError;
471    
472     # ifdef VSYNC_DEBUG
473     vblankinc++;
474     if( vblankinc > 1 )
475     {
476     if( hsc != vSyncInfo.hScanlinesPerFrame )
477     Console.WriteLn( " ** vSync > Abnormal Scanline Count: %d", hsc );
478     hsc = 0;
479     vblankinc = 0;
480     }
481     # endif
482     }
483     }
484    
485     static __forceinline void _cpuTestTarget( int i )
486     {
487     if (counters[i].count < counters[i].target) return;
488    
489     if(counters[i].mode.TargetInterrupt) {
490    
491     EECNT_LOG("EE Counter[%d] TARGET reached - mode=%x, count=%x, target=%x", i, counters[i].mode, counters[i].count, counters[i].target);
492     counters[i].mode.TargetReached = 1;
493     hwIntcIrq(counters[i].interrupt);
494    
495     // The PS2 only resets if the interrupt is enabled - Tested on PS2
496     if (counters[i].mode.ZeroReturn)
497     counters[i].count -= counters[i].target; // Reset on target
498     else
499     counters[i].target |= EECNT_FUTURE_TARGET;
500     }
501     else counters[i].target |= EECNT_FUTURE_TARGET;
502     }
503    
504     static __forceinline void _cpuTestOverflow( int i )
505     {
506     if (counters[i].count <= 0xffff) return;
507    
508     if (counters[i].mode.OverflowInterrupt) {
509     EECNT_LOG("EE Counter[%d] OVERFLOW - mode=%x, count=%x", i, counters[i].mode, counters[i].count);
510     counters[i].mode.OverflowReached = 1;
511     hwIntcIrq(counters[i].interrupt);
512     }
513    
514     // wrap counter back around zero, and enable the future target:
515     counters[i].count -= 0x10000;
516     counters[i].target &= 0xffff;
517     }
518    
519    
520     // forceinline note: this method is called from two locations, but one
521     // of them is the interpreter, which doesn't count. ;) So might as
522     // well forceinline it!
523     __forceinline void rcntUpdate()
524     {
525     rcntUpdate_vSync();
526    
527     // Update counters so that we can perform overflow and target tests.
528    
529     for (int i=0; i<=3; i++)
530     {
531     // We want to count gated counters (except the hblank which exclude below, and are
532     // counted by the hblank timer instead)
533    
534     //if ( gates & (1<<i) ) continue;
535    
536     if (!counters[i].mode.IsCounting ) continue;
537    
538     if(counters[i].mode.ClockSource != 0x3) // don't count hblank sources
539     {
540     s32 change = cpuRegs.cycle - counters[i].sCycleT;
541     if( change < 0 ) change = 0; // sanity check!
542    
543     counters[i].count += change / counters[i].rate;
544     change -= (change / counters[i].rate) * counters[i].rate;
545     counters[i].sCycleT = cpuRegs.cycle - change;
546    
547     // Check Counter Targets and Overflows:
548     _cpuTestTarget( i );
549     _cpuTestOverflow( i );
550     }
551     else counters[i].sCycleT = cpuRegs.cycle;
552     }
553    
554     cpuRcntSet();
555     }
556    
557     static __forceinline void _rcntSetGate( int index )
558     {
559     if (counters[index].mode.EnableGate)
560     {
561     // If the Gate Source is hblank and the clock selection is also hblank
562     // then the gate is disabled and the counter acts as a normal hblank source.
563    
564     if( !(counters[index].mode.GateSource == 0 && counters[index].mode.ClockSource == 3) )
565     {
566     EECNT_LOG( "EE Counter[%d] Using Gate! Source=%s, Mode=%d.",
567     index, counters[index].mode.GateSource ? "vblank" : "hblank", counters[index].mode.GateMode );
568    
569     gates |= (1<<index);
570     counters[index].mode.IsCounting = 0;
571     rcntReset(index);
572     return;
573     }
574     else
575     EECNT_LOG( "EE Counter[%d] GATE DISABLED because of hblank source.", index );
576     }
577    
578     gates &= ~(1<<index);
579     }
580    
581     // mode - 0 means hblank source, 8 means vblank source.
582     __forceinline void rcntStartGate(bool isVblank, u32 sCycle)
583     {
584     int i;
585    
586     for (i=0; i <=3; i++)
587     {
588     //if ((mode == 0) && ((counters[i].mode & 0x83) == 0x83))
589     if (!isVblank && counters[i].mode.IsCounting && (counters[i].mode.ClockSource == 3) )
590     {
591     // Update counters using the hblank as the clock. This keeps the hblank source
592     // nicely in sync with the counters and serves as an optimization also, since these
593     // counter won't recieve special rcntUpdate scheduling.
594    
595     // Note: Target and overflow tests must be done here since they won't be done
596     // currectly by rcntUpdate (since it's not being scheduled for these counters)
597    
598     counters[i].count += HBLANK_COUNTER_SPEED;
599     _cpuTestTarget( i );
600     _cpuTestOverflow( i );
601     }
602    
603     if (!(gates & (1<<i))) continue;
604     if ((!!counters[i].mode.GateSource) != isVblank) continue;
605    
606     switch (counters[i].mode.GateMode) {
607     case 0x0: //Count When Signal is low (off)
608    
609     // Just set the start cycle (sCycleT) -- counting will be done as needed
610     // for events (overflows, targets, mode changes, and the gate off below)
611    
612     counters[i].mode.IsCounting = 1;
613     counters[i].sCycleT = sCycle;
614     EECNT_LOG("EE Counter[%d] %s StartGate Type0, count = %x",
615     isVblank ? "vblank" : "hblank", i, counters[i].count );
616     break;
617    
618     case 0x2: // reset and start counting on vsync end
619     // this is the vsync start so do nothing.
620     break;
621    
622     case 0x1: //Reset and start counting on Vsync start
623     case 0x3: //Reset and start counting on Vsync start and end
624     counters[i].mode.IsCounting = 1;
625     counters[i].count = 0;
626     counters[i].target &= 0xffff;
627     counters[i].sCycleT = sCycle;
628     EECNT_LOG("EE Counter[%d] %s StartGate Type%d, count = %x",
629     isVblank ? "vblank" : "hblank", i, counters[i].mode.GateMode, counters[i].count );
630     break;
631     }
632     }
633    
634     // No need to update actual counts here. Counts are calculated as needed by reads to
635     // rcntRcount(). And so long as sCycleT is set properly, any targets or overflows
636     // will be scheduled and handled.
637    
638     // Note: No need to set counters here. They'll get set when control returns to
639     // rcntUpdate, since we're being called from there anyway.
640     }
641    
642     // mode - 0 means hblank signal, 8 means vblank signal.
643     __forceinline void rcntEndGate(bool isVblank , u32 sCycle)
644     {
645     int i;
646    
647     for(i=0; i <=3; i++) { //Gates for counters
648     if (!(gates & (1<<i))) continue;
649     if ((!!counters[i].mode.GateSource) != isVblank) continue;
650    
651     switch (counters[i].mode.GateMode) {
652     case 0x0: //Count When Signal is low (off)
653    
654     // Set the count here. Since the timer is being turned off it's
655     // important to record its count at this point (it won't be counted by
656     // calls to rcntUpdate).
657    
658     counters[i].count = rcntRcount(i);
659     counters[i].mode.IsCounting = 0;
660     counters[i].sCycleT = sCycle;
661     EECNT_LOG("EE Counter[%d] %s EndGate Type0, count = %x",
662     isVblank ? "vblank" : "hblank", i, counters[i].count );
663     break;
664    
665     case 0x1: // Reset and start counting on Vsync start
666     // this is the vsync end so do nothing
667     break;
668    
669     case 0x2: //Reset and start counting on Vsync end
670     case 0x3: //Reset and start counting on Vsync start and end
671     counters[i].mode.IsCounting = 1;
672     counters[i].count = 0;
673     counters[i].target &= 0xffff;
674     counters[i].sCycleT = sCycle;
675     EECNT_LOG("EE Counter[%d] %s EndGate Type%d, count = %x",
676     isVblank ? "vblank" : "hblank", i, counters[i].mode.GateMode, counters[i].count );
677     break;
678     }
679     }
680     // Note: No need to set counters here. They'll get set when control returns to
681     // rcntUpdate, since we're being called from there anyway.
682     }
683    
684     __forceinline void rcntWmode(int index, u32 value)
685     {
686     if(counters[index].mode.IsCounting) {
687     if(counters[index].mode.ClockSource != 0x3) {
688    
689     u32 change = cpuRegs.cycle - counters[index].sCycleT;
690     if( change > 0 )
691     {
692     counters[index].count += change / counters[index].rate;
693     change -= (change / counters[index].rate) * counters[index].rate;
694     counters[index].sCycleT = cpuRegs.cycle - change;
695     }
696     }
697     }
698     else counters[index].sCycleT = cpuRegs.cycle;
699    
700     // Clear OverflowReached and TargetReached flags (0xc00 mask), but *only* if they are set to 1 in the
701     // given value. (yes, the bits are cleared when written with '1's).
702    
703     counters[index].modeval &= ~(value & 0xc00);
704     counters[index].modeval = (counters[index].modeval & 0xc00) | (value & 0x3ff);
705     EECNT_LOG("EE Counter[%d] writeMode = %x passed value=%x", index, counters[index].modeval, value );
706    
707     switch (counters[index].mode.ClockSource) { //Clock rate divisers *2, they use BUSCLK speed not PS2CLK
708     case 0: counters[index].rate = 2; break;
709     case 1: counters[index].rate = 32; break;
710     case 2: counters[index].rate = 512; break;
711     case 3: counters[index].rate = vSyncInfo.hBlank+vSyncInfo.hRender; break;
712     }
713    
714     _rcntSetGate( index );
715     _rcntSet( index );
716     }
717    
718     __forceinline void rcntWcount(int index, u32 value)
719     {
720     EECNT_LOG("EE Counter[%d] writeCount = %x, oldcount=%x, target=%x", index, value, counters[index].count, counters[index].target );
721    
722     counters[index].count = value & 0xffff;
723    
724     // reset the target, and make sure we don't get a premature target.
725     counters[index].target &= 0xffff;
726     if( counters[index].count > counters[index].target )
727     counters[index].target |= EECNT_FUTURE_TARGET;
728    
729     // re-calculate the start cycle of the counter based on elapsed time since the last counter update:
730     if(counters[index].mode.IsCounting) {
731     if(counters[index].mode.ClockSource != 0x3) {
732     s32 change = cpuRegs.cycle - counters[index].sCycleT;
733     if( change > 0 ) {
734     change -= (change / counters[index].rate) * counters[index].rate;
735     counters[index].sCycleT = cpuRegs.cycle - change;
736     }
737     }
738     }
739     else counters[index].sCycleT = cpuRegs.cycle;
740    
741     _rcntSet( index );
742     }
743    
744     __forceinline void rcntWtarget(int index, u32 value)
745     {
746     EECNT_LOG("EE Counter[%d] writeTarget = %x", index, value);
747    
748     counters[index].target = value & 0xffff;
749    
750     // guard against premature (instant) targeting.
751     // If the target is behind the current count, set it up so that the counter must
752     // overflow first before the target fires:
753    
754     if(counters[index].mode.IsCounting) {
755     if(counters[index].mode.ClockSource != 0x3) {
756    
757     u32 change = cpuRegs.cycle - counters[index].sCycleT;
758     if( change > 0 )
759     {
760     counters[index].count += change / counters[index].rate;
761     change -= (change / counters[index].rate) * counters[index].rate;
762     counters[index].sCycleT = cpuRegs.cycle - change;
763     }
764     }
765     }
766    
767     if( counters[index].target <= rcntCycle(index) )
768     counters[index].target |= EECNT_FUTURE_TARGET;
769    
770     _rcntSet( index );
771     }
772    
773     __forceinline void rcntWhold(int index, u32 value)
774     {
775     EECNT_LOG("EE Counter[%d] Hold Write = %x", index, value);
776     counters[index].hold = value;
777     }
778    
779     __forceinline u32 rcntRcount(int index)
780     {
781     u32 ret;
782    
783     // only count if the counter is turned on (0x80) and is not an hsync gate (!0x03)
784     if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3))
785     ret = counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate);
786     else
787     ret = counters[index].count;
788    
789     // Spams the Console.
790     EECNT_LOG("EE Counter[%d] readCount32 = %x", index, ret);
791     return ret;
792     }
793    
794     __forceinline u32 rcntCycle(int index)
795     {
796     if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3))
797     return counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate);
798     else
799     return counters[index].count;
800     }
801    
802     void SaveStateBase::rcntFreeze()
803     {
804     Freeze( counters );
805     Freeze( hsyncCounter );
806     Freeze( vsyncCounter );
807     Freeze( nextCounter );
808     Freeze( nextsCounter );
809    
810     if( IsLoading() )
811     {
812     UpdateVSyncRate();
813    
814     // make sure the gate flags are set based on the counter modes...
815     for( int i=0; i<4; i++ )
816     _rcntSetGate( i );
817    
818     iopBranchAction = 1; // probably not needed but won't hurt anything either.
819     }
820     }

  ViewVC Help
Powered by ViewVC 1.1.22