/[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 31 - (show annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 10 months ago) by william
File size: 25601 byte(s)
committing r3113 initial commit again...
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 #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