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

Contents of /trunk/pcsx2/Counters.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 273 - (show annotations) (download)
Fri Nov 12 01:10:22 2010 UTC (9 years, 3 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 /* 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 #include "Common.h"
23 #include "R3000A.h"
24 #include "Counters.h"
25 #include "IopCounters.h"
26
27 #include "GS.h"
28 #include "VUmicro.h"
29
30 #include "ps2/HwInternal.h"
31
32 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 // Forward declarations needed because C/C++ both are wimpy single-pass compilers.
51
52 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 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 static __fi void _rcntSet( int cntidx )
69 {
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 cpuSetNextEvent( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
97 }
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 cpuSetNextEvent( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
115 }
116 }
117 }
118
119
120 static __fi void cpuRcntSet()
121 {
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 static __fi void frameLimit()
301 {
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 static __fi void VSyncStart(u32 sCycle)
346 {
347 GetCoreThread().VsyncInThread();
348 Cpu->CheckExecutionState();
349
350 if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
351 SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount );
352
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 if (!CSRreg.VSINT)
362 {
363 CSRreg.VSINT = true;
364 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 static __fi void VSyncEnd(u32 sCycle)
396 {
397 if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
398 SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC END (frame: %d) ================", g_FrameCount );
399
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 __fi void rcntUpdate_hScanline()
421 {
422 if( !cpuTestCycle( hsyncCounter.sCycle, hsyncCounter.CycleT ) ) return;
423
424 //iopEventAction = 1;
425 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 if (!CSRreg.HSINT)
436 {
437 CSRreg.HSINT = true;
438 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 __fi void rcntUpdate_vSync()
458 {
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 static __fi void _cpuTestTarget( int i )
495 {
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 static __fi void _cpuTestOverflow( int i )
514 {
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 __fi void rcntUpdate()
533 {
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 static __fi void _rcntSetGate( int index )
567 {
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 static __fi void rcntStartGate(bool isVblank, u32 sCycle)
592 {
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 static __fi void rcntEndGate(bool isVblank , u32 sCycle)
653 {
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 static __fi u32 rcntCycle(int index)
694 {
695 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 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 static __fi void rcntWcount(int index, u32 value)
736 {
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 static __fi void rcntWtarget(int index, u32 value)
762 {
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 static __fi void rcntWhold(int index, u32 value)
791 {
792 EECNT_LOG("EE Counter[%d] Hold Write = %x", index, value);
793 counters[index].hold = value;
794 }
795
796 __fi u32 rcntRcount(int index)
797 {
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 template< uint page >
812 __fi u16 rcntRead32( u32 mem )
813 {
814 // 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 }
841
842 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 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 iopEventAction = 1; // probably not needed but won't hurt anything either.
896 }
897 }

  ViewVC Help
Powered by ViewVC 1.1.22