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

  ViewVC Help
Powered by ViewVC 1.1.22