From f6ecf0bd68512619710b97d530cb28f6d377fe24 Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Wed, 19 Nov 2008 21:23:55 +0000 Subject: [PATCH] New EE/IOP synchronization system -- found the root cause of many synchronization problems by issuing an IOP branch test after BIOS calls. That allowed me to get rid of the overly complicated adaptive EE_WAIT_CYCLES mess from r291 and replace it with a much cleaner and more efficient branching system. Also fixed a few bugs where-by the IOP would run waaaay ahead of the EE (leading to skippy sound, slowdowns, other things), and also a bug that caused crashes when resetting or starting new games. Note: New IOP Counters code is a Work-in-Progress. Should be fine for most games, but if a game uses the IOP counter gates it might break. Will finish them soon! git-svn-id: http://pcsx2-playground.googlecode.com/svn/trunk@345 a6443dda-0b58-4228-96e9-037be469359c --- pcsx2/Common.h | 37 -- pcsx2/Counters.c | 541 ++++++++++------- pcsx2/Counters.h | 46 +- pcsx2/Hw.c | 7 +- pcsx2/Hw.h | 10 +- pcsx2/PsxCounters.c | 1042 ++++++++++++++++----------------- pcsx2/PsxCounters.h | 9 +- pcsx2/PsxHw.h | 2 +- pcsx2/PsxInterpreter.c | 2 +- pcsx2/R3000A.c | 101 ++-- pcsx2/R3000A.h | 8 +- pcsx2/R5900.c | 333 +++++------ pcsx2/R5900.h | 7 +- pcsx2/x86/iR3000A.cpp | 19 +- pcsx2/x86/ix86-64/iR5900-64.c | 11 +- 15 files changed, 1112 insertions(+), 1063 deletions(-) diff --git a/pcsx2/Common.h b/pcsx2/Common.h index ca6f28e816..0b86bd2932 100644 --- a/pcsx2/Common.h +++ b/pcsx2/Common.h @@ -157,43 +157,6 @@ extern TESTRUNARGS g_TestRun; VBlank non-interlaced 59.82 Hz HBlank 15.73426573 KHz */ -//VBlanks per second -#define VBLANK_NTSC ((Config.PsxType & 2) ? 59.94 : 59.82) //59.94005994 is more precise -#define VBLANK_PAL ((Config.PsxType & 2) ? 50.00 : 49.76) - -//HBlanks per second -#define HBLANK_NTSC (15734.26573) -#define HBLANK_PAL (15625) - -//VBlank timers for EE, bit more accurate. -#define VBLANKCNT(count) ((u32)((Config.PsxType & 1) ? (VBLANKPALSELECT * count) : (VBLANKNTSCSELECT * count))) -#define VBLANKPALSELECT ((Config.PsxType & 2) ? (PS2CLK / 50.00) : (PS2CLK / 49.76)) -#define VBLANKNTSCSELECT ((Config.PsxType & 2) ? (PS2CLK / 59.94) : (PS2CLK / 59.82)) //59.94005994 is more precise - -//EE VBlank speeds -#define PS2VBLANK_NTSC_INT ((PS2CLK / 59.94005994)) -#define PS2VBLANK_NTSC ((PS2CLK / 59.82)) -#define PS2VBLANK_PAL_INT ((PS2CLK / 50.00)) -#define PS2VBLANK_PAL ((PS2CLK / 49.76)) - -//HBlank timer for EE, bit more accurate. -#define HBLANKCNT(count) ((u32)(PS2HBLANK * count)) - -//EE HBlank speeds -#define PS2HBLANK_NTSC ((int)(PS2CLK / HBLANK_NTSC)) -#define PS2HBLANK_PAL ((int)(PS2CLK / HBLANK_PAL)) -#define PS2HBLANK ((int)((Config.PsxType & 1) ? PS2HBLANK_PAL : PS2HBLANK_NTSC)) - -//IOP VBlank speeds -#define PSXVBLANK_NTSC ((int)(PSXCLK / VBLANK_NTSC)) -#define PSXVBLANK_PAL ((int)(PSXCLK / VBLANK_PAL)) -#define PSXVBLANK ((int)((Config.PsxType & 1) ? PSXVBLANK_PAL : PSXVBLANK_NTSC)) - -//IOP HBlank speeds -#define PSXHBLANK_NTSC ((int)(PSXCLK / HBLANK_NTSC)) -#define PSXHBLANK_PAL ((int)(PSXCLK / HBLANK_PAL)) -#define PSXHBLANK ((int)((Config.PsxType & 1) ? PSXHBLANK_PAL : PSXHBLANK_NTSC)) - //Misc Clocks #define PSXPIXEL ((int)(PSXCLK / 13500000)) #define PSXSOUNDCLK ((int)(48000)) diff --git a/pcsx2/Counters.c b/pcsx2/Counters.c index b51e435908..f5393d779e 100644 --- a/pcsx2/Counters.c +++ b/pcsx2/Counters.c @@ -29,23 +29,20 @@ u64 profile_totalticks = 0; int gates = 0; extern u8 psxhblankgate; + +// Counter 4 takes care of scanlines - hSync/hBlanks +// Counter 5 takes care of vSync/vBlanks Counter counters[6]; -u32 nextCounter, nextsCounter; + +u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate() +s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate() + static void (*s_prevExecuteVU1Block)() = NULL; LARGE_INTEGER lfreq; -void rcntUpdTarget(int index) { - counters[index].sCycleT = cpuRegs.cycle; -} - -void rcntUpd(int index) { - counters[index].sCycle = cpuRegs.cycle; - rcntUpdTarget(index); -} - void rcntReset(int index) { counters[index].count = 0; - rcntUpd(index); + counters[index].sCycleT = cpuRegs.cycle; } // Updates the state of the nextCounter value (if needed) to serve @@ -53,8 +50,11 @@ void rcntReset(int index) { // Call this method after any modifications to the state of a counter. static __forceinline void _rcntSet( int i ) { - u32 c; - if (!(counters[i].mode & 0x80) || (counters[i].mode & 0x3) == 0x3) return; // Stopped + s32 c; + assert( i <= 4 ); // rcntSet isn't valid for h/vsync counters. + + // Stopped or special hsync gate? + if (!(counters[i].mode & 0x80) || (counters[i].mode & 0x3) == 0x3) return; // nextCounter is relative to the cpuRegs.cycle when rcntUpdate() was last called. // However, the current _rcntSet could be called at any cycle count, so we need to take @@ -65,31 +65,37 @@ static __forceinline void _rcntSet( int i ) c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate(); if (c < nextCounter) nextCounter = c; - //if(!(counters[i].mode & 0x100) || counters[i].target > 0xffff) continue; + // Ignore target diff if target is currently disabled. + // (the overflow is all we care about since it goes first, and then the + // target will be turned on afterward). + if( counters[i].target & 0x10000000 ) return; c = ((counters[i].target - counters[i].count) * counters[i].rate) - (cpuRegs.cycle - counters[i].sCycleT); c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate(); if (c < nextCounter) nextCounter = c; } -static __forceinline void cpuRcntSet() { - // Calculate our target cycle deltas. - // This must be done regardless of if the hblank/vblank counters updated since - // cpuRegs.cycle changes, even if sCycle hasn't! - - u32 counter4CycleT = ( counters[4].mode == MODE_HBLANK ) ? HBLANK_TIME_ : HRENDER_TIME_; - u32 counter5CycleT = VSYNC_HALF_ - (cpuRegs.cycle - counters[5].sCycle); - counter4CycleT -= (cpuRegs.cycle - counters[4].sCycle); - - nextCounter = (counter4CycleT < counter5CycleT) ? counter4CycleT : counter5CycleT; +static __forceinline void cpuRcntSet() +{ + int i; nextsCounter = cpuRegs.cycle; - - _rcntSet( 0 ); - _rcntSet( 1 ); - _rcntSet( 2 ); - _rcntSet( 3 ); + nextCounter = (counters[5].sCycle + counters[5].CycleT) - cpuRegs.cycle; + + // if we're running behind, the diff will be negative. + // (and running behind means we need to branch again ASAP) + if( nextCounter <= 0 ) + { + nextCounter = 0; + return; + } + + for (i = 0; i < 4; i++) + _rcntSet( i ); + + // sanity check! + if( nextCounter < 0 ) nextCounter = 0; } void rcntInit() { @@ -106,13 +112,18 @@ void rcntInit() { counters[2].interrupt = 11; counters[3].interrupt = 12; + counters[4].mode = MODE_HRENDER; + counters[4].sCycle = cpuRegs.cycle; + counters[5].mode = MODE_VRENDER; + counters[5].sCycle = cpuRegs.cycle; + UpdateVSyncRate(); #ifdef _WIN32 QueryPerformanceFrequency(&lfreq); #endif - for (i=0; i<4; i++) rcntUpd(i); + for (i=0; i<4; i++) rcntReset(i); cpuRcntSet(); assert(Cpu != NULL && Cpu->ExecuteVU1Block != NULL ); @@ -151,29 +162,120 @@ u64 GetCPUTicks() #endif } -void UpdateVSyncRate() { +typedef struct +{ + u32 Framerate; // frames per second * 100 (so 2500 for PAL and 2997 for NTSC) + u32 Render; // time from vblank end to vblank start (cycles) + u32 Blank; // time from vblank start to vblank end (cycles) - counters[4].mode = MODE_HRENDER; // Counter 4 takes care of scanlines, so set the mode to HRENDER (drawing part of scanline) - counters[4].sCycle = cpuRegs.cycle; // Update Counter 4's Start Cycle to match CPU's cycle - counters[4].CycleT = HRENDER_TIME_; // Amount of cycles before the counter will be updated - - counters[5].mode = MODE_VRENDER; // Counter 5 takes care of vSync/vBlanks - counters[5].sCycle = cpuRegs.cycle; // Update Counter 5's Start Cycle to match CPU's cycle - counters[5].CycleT = VSYNC_HALF_; // Amount of cycles before the counter will be updated + u32 hSyncError; // rounding error after the duration of a rendered frame (cycles) + u32 hRender; // time from hblank end to hblank start (cycles) + u32 hBlank; // time from hblank start to hblank end (cycles) + u32 hScanlinesPerFrame; // number of scanlines per frame (525/625 for NTSC/PAL) +} vSyncTimingInfo; - if (Config.CustomFps > 0) { - iTicks = GetTickFrequency() / Config.CustomFps; - SysPrintf("Framelimiter rate updated (UpdateVSyncRate): %d fps\n", Config.CustomFps); +static vSyncTimingInfo vSyncInfo; + + +static __forceinline void vSyncInfoCalc( vSyncTimingInfo* info, u32 framesPerSecond, u32 scansPerFrame ) +{ + // Important: Cannot use floats or doubles here. The emulator changes rounding modes + // depending on user-set speedhack options, and it can break float/double code + // (as in returning infinities and junk) + + u64 Frame = ((u64)PS2CLK * 1000000ULL) / framesPerSecond; + u64 HalfFrame = Frame / 2; + u64 Blank = HalfFrame / 4; // two blanks and renders per frame + u64 Render = HalfFrame - Blank; // so use the half-frame value for these... + + // Important! The hRender/hBlank timers should be 50/50 for best results. + // In theory a 70%/30% ratio would be more correct but in practice it runs + // like crap and totally screws audio synchronization and other things. + + u64 Scanline = Frame / scansPerFrame; + u64 hBlank = Scanline / 2; + u64 hRender = Scanline - hBlank; + + info->Framerate = framesPerSecond; + info->Render = (u32)(Render/10000); + info->Blank = (u32)(Blank/10000); + + info->hRender = (u32)(hRender/10000); + info->hBlank = (u32)(hBlank/10000); + info->hScanlinesPerFrame = scansPerFrame; + + // Apply rounding: + if( ( Render - info->Render ) >= 5000 ) info->Render++; + else if( ( Blank - info->Blank ) >= 5000 ) info->Blank++; + + if( ( hRender - info->hRender ) >= 5000 ) info->hRender++; + else if( ( hBlank - info->hBlank ) >= 5000 ) info->hBlank++; + + // Calculate accumulative hSync rounding error per half-frame: + { + u32 hSyncCycles = ((info->hRender + info->hBlank) * scansPerFrame) / 2; + u32 vSyncCycles = (info->Render + info->Blank); + info->hSyncError = vSyncCycles - hSyncCycles; } - else if (Config.PsxType & 1) { - iTicks = (GetTickFrequency() * 100) / 5000; - SysPrintf("Framelimiter rate updated (UpdateVSyncRate): 50 fps Pal\n"); + + // Note: In NTSC modes there is some small rounding error in the vsync too, + // however it would take thousands of frames for it to amount to anything and + // is thus not worth the effort at this time. +} + + +void UpdateVSyncRate() +{ + const char *limiterMsg = "Framelimiter rate updated (UpdateVSyncRate): %s fps\n"; + + // fixme - According to some docs, progressive-scan modes actually refresh slower than + // interlaced modes. But I can't fathom how, since the refresh rate is a function of + // the television and all the docs I found on TVS made no indication that they ever + // run anything except their native refresh rate. + + //#define VBLANK_NTSC ((Config.PsxType & 2) ? 59.94 : 59.82) //59.94 is more precise + //#define VBLANK_PAL ((Config.PsxType & 2) ? 50.00 : 49.76) + + if(Config.PsxType & 1) + { + if( vSyncInfo.Framerate != FRAMERATE_PAL ) + { + SysPrintf( "PCSX2: Switching to PAL display timings.\n" ); + vSyncInfoCalc( &vSyncInfo, FRAMERATE_PAL, SCANLINES_TOTAL_PAL ); + } } - else { - iTicks = (GetTickFrequency() / 5994) * 100; - SysPrintf("Framelimiter rate updated (UpdateVSyncRate): 59.94 fps NTSC\n"); + else + { + if( vSyncInfo.Framerate != FRAMERATE_NTSC ) + { + SysPrintf( "PCSX2: Switching to NTSC display timings.\n" ); + vSyncInfoCalc( &vSyncInfo, FRAMERATE_NTSC, SCANLINES_TOTAL_NTSC ); + } } + + counters[4].CycleT = vSyncInfo.hRender; // Amount of cycles before the counter will be updated + counters[5].CycleT = vSyncInfo.Render; // Amount of cycles before the counter will be updated + + if (Config.CustomFps > 0) + { + u32 ticks = (u32)(GetTickFrequency() / Config.CustomFps); + if( iTicks != ticks ) + { + iTicks = ticks; + SysPrintf( limiterMsg, Config.CustomFps ); + } + } + else + { + u32 ticks = (u32)((GetTickFrequency() * 50) / vSyncInfo.Framerate); + if( iTicks != ticks ) + { + iTicks = ticks; + SysPrintf( limiterMsg, (Config.PsxType & 1) ? "50" : "59.94" ); + } + } + cpuRcntSet(); } @@ -189,7 +291,10 @@ void FrameLimiter() if (iEnd>=iExpectedEnd) { // Compensation: If the framelate drops too low, reset the - // expected value. This avoids "fast forward" syndrome. + // expected value. This avoids excessive amounts of + // "fast forward" syndrome which would occur if we tried to + // catch up too much. + u64 diff = iEnd-iExpectedEnd; if ((diff>>3)>iTicks) iExpectedEnd=iEnd; } @@ -209,8 +314,6 @@ extern u32 vu0time; extern void DummyExecuteVU1Block(void); -//static u32 lastWasSkip=0; -//extern u32 unpacktotal; void vSyncDebugStuff() { #ifdef EE_PROFILING @@ -394,104 +497,102 @@ static __forceinline void VSyncEnd(u32 sCycle) // VSync End frameLimit(); // limit FPS (also handles frameskip and VUskip) } -static __forceinline void hScanlineNextCycle( u32 diff, u32 cyclesAmount ) +#ifndef PCSX2_PUBLIC +static u32 hsc=0; +static int vblankinc = 0; +#endif + +__forceinline void rcntUpdate_hScanline() { - // This function: Now Unneeded? - // This code doesn't appear to be run anymore after fixing the CycleT bug - // and fixing the EE/IOP code execution sync issues (tested on 6 games, - // with EEx3 hack too). - - // And it makes sense -- bad behavior by the counters would have led - // to cpuBranchTest being delayed beyond the span of a full hsync. - // It could still happen in some isolated part of some particular game, - // but probably we're better off letting that game lose a couple hsyncs - // once in a while rather than slow everyone else down needlessly. - - u32 scanlineCycles = SCANLINE_; - diff -= cyclesAmount; - if (diff >= scanlineCycles) - { - u32 increment = diff / scanlineCycles; - - // Counter Optimization: - // If the time passed is beyond a single scanline, then increment all scanline - // counters as a set here. - - SysPrintf("Counters Optimization %d\n", diff / scanlineCycles); - - /* if counter's count increases on hblank gate's off signal OR if counter increases every hblank, THEN add to the counter */ - if ( (!(counters[0].mode & 0x30) && (gates & (1<<0))) || (((counters[0].mode & 0x83) == 0x83) && !(gates & (1<<0))) ) counters[0].count += (increment * HBLANK_COUNTER_SPEED); - if ( (!(counters[1].mode & 0x30) && (gates & (1<<1))) || (((counters[1].mode & 0x83) == 0x83) && !(gates & (1<<1))) ) counters[1].count += (increment * HBLANK_COUNTER_SPEED); - if ( (!(counters[2].mode & 0x30) && (gates & (1<<2))) || (((counters[2].mode & 0x83) == 0x83) && !(gates & (1<<2))) ) counters[2].count += (increment * HBLANK_COUNTER_SPEED); - if ( (!(counters[3].mode & 0x30) && (gates & (1<<3))) || (((counters[3].mode & 0x83) == 0x83) && !(gates & (1<<3))) ) counters[3].count += (increment * HBLANK_COUNTER_SPEED); - counters[4].sCycle += (increment * scanlineCycles); - } -} - -static __forceinline void hScanline() -{ - u32 difference = (cpuRegs.cycle - counters[4].sCycle); + if( !cpuTestCycle( counters[4].sCycle, counters[4].CycleT ) ) return; + iopBranchAction = 1; if (counters[4].mode & MODE_HBLANK) { //HBLANK Start - const u32 modeCycles = HBLANK_TIME_; - if (difference >= modeCycles ) { - //hScanlineNextCycle(difference, modeCycles); - counters[4].sCycle += modeCycles; - rcntStartGate(0, counters[4].sCycle); - psxCheckStartGate16(0); - counters[4].mode = MODE_HRENDER; - } + //hScanlineNextCycle(difference, modeCycles); + rcntStartGate(0, counters[4].sCycle); + psxCheckStartGate16(0); + + // Setup the hRender's start and end cycle information: + counters[4].sCycle += vSyncInfo.hBlank; // start (absolute cycle value) + counters[4].CycleT = vSyncInfo.hRender; // endpoint (delta from start value) + counters[4].mode = MODE_HRENDER; } else { //HBLANK END / HRENDER Begin - const u32 modeCycles = HRENDER_TIME_; - if (difference >= modeCycles) { - //hScanlineNextCycle(difference, modeCycles); - counters[4].sCycle += modeCycles; - if (CSRw & 0x4) GSCSRr |= 4; // signal - if (!(GSIMR&0x400)) gsIrq(); - if (gates) rcntEndGate(0, counters[4].sCycle); - if (psxhblankgate) psxCheckEndGate16(0); - counters[4].mode = MODE_HBLANK; - } + //hScanlineNextCycle(difference, modeCycles); + if (CSRw & 0x4) GSCSRr |= 4; // signal + if (!(GSIMR&0x400)) gsIrq(); + if (gates) rcntEndGate(0, counters[4].sCycle); + if (psxhblankgate) psxCheckEndGate16(0); + + // set up the hblank's start and end cycle information: + counters[4].sCycle += vSyncInfo.hRender; // start (absolute cycle value) + counters[4].CycleT = vSyncInfo.hBlank; // endpoint (delta from start value) + counters[4].mode = MODE_HBLANK; + +# ifndef PCSX2_PUBLIC + hsc++; +# endif } - - /*if(counters[4].CycleT < 0) { - counters[4].sCycle += -counters[4].CycleT; - counters[4].CycleT = 0; - }*/ - } -// Only called from one place so might as well inline it. -static __forceinline void vSync() +__forceinline void rcntUpdate_vSync() { - u32 diff = (cpuRegs.cycle - counters[5].sCycle); + s32 diff = (cpuRegs.cycle - counters[5].sCycle); + if( diff < counters[5].CycleT ) return; - hScanline(); + iopBranchAction = 1; + if (counters[5].mode == MODE_VSYNC) + { + VSyncEnd(counters[5].sCycle); - if (diff >= (VSYNC_HALF_)) { - counters[5].sCycle += VSYNC_HALF_; // * (u32)(diff / VSYNC_HALF_)); + counters[5].sCycle += vSyncInfo.Blank; + counters[5].CycleT = vSyncInfo.Render; + counters[5].mode = MODE_VRENDER; + } + else // VSYNC end / VRENDER begin + { + VSyncStart(counters[5].sCycle); - if (counters[5].mode == MODE_VSYNC) { - VSyncEnd(counters[5].sCycle); - counters[5].mode = MODE_VRENDER; + counters[5].sCycle += vSyncInfo.Render; + counters[5].CycleT = vSyncInfo.Blank; + counters[5].mode = MODE_VSYNC; + + // Accumulate hsync rounding errors: + counters[4].sCycle += vSyncInfo.hSyncError; + +# ifndef PCSX2_PUBLIC + vblankinc++; + if( vblankinc > 1 ) + { + if( hsc != vSyncInfo.hScanlinesPerFrame ) + SysPrintf( " ** vSync > Abnornal Scanline Count: %d\n", hsc ); + hsc = 0; + vblankinc = 0; } - else { - VSyncStart(counters[5].sCycle); - counters[5].mode = MODE_VSYNC; +# endif - // Accumulate hsync rounding errors: - counters[4].sCycle += HSYNC_ERROR; - - // Tighten up EE/IOP responsiveness for a wee bit. - // Games are usually most sensitive to vSync sessions since that's - // when the hard thinking usually occurs. - g_eeTightenSync += 2; - } - g_nextBranchCycle = cpuRegs.cycle + 384; } } +static __forceinline void __fastcall _cpuTestTarget( int i ) +{ + //counters[i].target &= 0xffff; + + if(counters[i].mode & 0x100) { + + EECNT_LOG("EE counter %d target reached mode %x count %x target %x\n", i, counters[i].mode, counters[i].count, counters[i].target); + counters[i].mode|= 0x0400; // Equal Target flag + hwIntcIrq(counters[i].interrupt); + + if (counters[i].mode & 0x40) { //The PS2 only resets if the interrupt is enabled - Tested on PS2 + counters[i].count -= counters[i].target; // Reset on target + } + else counters[i].target |= 0x10000000; + } + else counters[i].target |= 0x10000000; +} + + // forceinline note: this method is called from two locations, but one // of them is the interpreter, which doesn't count. ;) So might as // well forceinline it! @@ -499,42 +600,36 @@ __forceinline void rcntUpdate() { int i; - vSync(); //hBlank and vSync Timers + rcntUpdate_vSync(); + + // Update all counters? + // This code shouldn't be needed. Counters are updated as needed when + // Reads, Writes, and Target/Overflow events occur. The rest of the + // time the counters can be left unmodified. for (i=0; i<=3; i++) { if ( gates & (1< 0 ) { + counters[i].count += change / counters[i].rate; + change -= (change / counters[i].rate) * counters[i].rate; + counters[i].sCycleT = cpuRegs.cycle - change; + } } else counters[i].sCycleT = cpuRegs.cycle; } - for (i=0; i<=3; i++) { + // Check Counter Targets and Overflows: + + for (i=0; i<=3; i++) + { if (!(counters[i].mode & 0x80)) continue; // Stopped - if (counters[i].count >= counters[i].target) { // Target interrupt - - counters[i].target &= 0xffff; - - if(counters[i].mode & 0x100) { - - EECNT_LOG("EE counter %d target reached mode %x count %x target %x\n", i, counters[i].mode, counters[i].count, counters[i].target); - counters[i].mode|= 0x0400; // Equal Target flag - hwIntcIrq(counters[i].interrupt); - - if (counters[i].mode & 0x40) { //The PS2 only resets if the interrupt is enabled - Tested on PS2 - counters[i].count -= counters[i].target; // Reset on target - } - else counters[i].target |= 0x10000000; - } - else counters[i].target |= 0x10000000; - } + // Target reached? + if (counters[i].count >= counters[i].target) + _cpuTestTarget( i ); if (counters[i].count > 0xffff) { @@ -545,9 +640,14 @@ __forceinline void rcntUpdate() } counters[i].count -= 0x10000; counters[i].target &= 0xffff; + + // Target reached after overflow? + // It's possible that a Target very near zero (1-10, etc) could have already been reached. + // Checking for it now + //if (counters[i].count >= counters[i].target) + // _cpuTestTarget( i ); } } - cpuRcntSet(); } @@ -556,15 +656,17 @@ void rcntWcount(int index, u32 value) EECNT_LOG("EE count write %d count %x with %x target %x eecycle %x\n", index, counters[index].count, value, counters[index].target, cpuRegs.eCycle); counters[index].count = value & 0xffff; counters[index].target &= 0xffff; - //rcntUpd(index); - if((counters[index].mode & 0x3) != 0x3) { - //counters[index].sCycleT = cpuRegs.cycle; - - u32 change = cpuRegs.cycle - counters[index].sCycleT; - change -= (change / counters[index].rate) * counters[index].rate; - counters[index].sCycleT = cpuRegs.cycle - change; - } + if(counters[index].mode & 0x80) { + if((counters[index].mode & 0x3) != 0x3) { + s32 change = cpuRegs.cycle - counters[index].sCycleT; + if( change > 0 ) { + change -= (change / counters[index].rate) * counters[index].rate; + counters[index].sCycleT = cpuRegs.cycle - change; + } + } + } + else counters[index].sCycleT = cpuRegs.cycle; _rcntSet( index ); } @@ -573,13 +675,14 @@ void rcntWmode(int index, u32 value) { if(counters[index].mode & 0x80) { if((counters[index].mode & 0x3) != 0x3) { - //counters[index].count += (cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate; - //counters[index].sCycleT = cpuRegs.cycle; - + u32 change = cpuRegs.cycle - counters[index].sCycleT; - counters[index].count += (int)(change / counters[index].rate); - change -= (change / counters[index].rate) * counters[index].rate; - counters[index].sCycleT = cpuRegs.cycle - change; + if( change > 0 ) + { + counters[index].count += change / counters[index].rate; + change -= (change / counters[index].rate) * counters[index].rate; + counters[index].sCycleT = cpuRegs.cycle - change; + } } } else counters[index].sCycleT = cpuRegs.cycle; @@ -592,7 +695,7 @@ void rcntWmode(int index, u32 value) case 0: counters[index].rate = 2; break; case 1: counters[index].rate = 32; break; case 2: counters[index].rate = 512; break; - case 3: counters[index].rate = SCANLINE_; break; + case 3: counters[index].rate = vSyncInfo.hBlank+vSyncInfo.hRender; break; } if((counters[index].mode & 0xF) == 0x7) { @@ -607,11 +710,6 @@ void rcntWmode(int index, u32 value) } else gates &= ~(1<=3) ? 0x100000000ULL : 0x10000; u64 c; -#ifdef _DEBUG - if(bitwise == 32) - assert( (i < 6) && (i >= 3) ); - else - assert( i < 3 ); -#endif - // psxNextCounter is relative to the psxRegs.cycle when rcntUpdate() was last called. // However, the current _rcntSet could be called at any cycle count, so we need to take // that into account. Adding the difference from that cycle count to the current one // will do the trick! - if(psxCounters[i].rate == PSXHBLANK) return; + if( psxCounters[i].mode & IOPCNT_GATE_STOPPED || psxCounters[i].rate == PSXHBLANK) return; c = (u64)((overflowCap - psxCounters[i].count) * psxCounters[i].rate) - (psxRegs.cycle - psxCounters[i].sCycleT); c += psxRegs.cycle - psxNextsCounter; // adjust for time passed since last rcntUpdate(); - if (c < psxNextCounter) psxNextCounter = (u32)c; + if((u64)c < (u64)psxNextCounter) psxNextCounter = (u32)c; //if((psxCounters[i].mode & 0x10) == 0 || psxCounters[i].target > 0xffff) continue; + if( psxCounters[i].target & IOPCNT_FUTURE_TARGET ) return; c = (u64)((psxCounters[i].target - psxCounters[i].count) * psxCounters[i].rate) - (psxRegs.cycle - psxCounters[i].sCycleT); c += psxRegs.cycle - psxNextsCounter; // adjust for time passed since last rcntUpdate(); - if (c < psxNextCounter) psxNextCounter = (u32)c; -} - -static void psxRcntSet() { - int i; - - psxNextCounter = 0xffffffff; - psxNextsCounter = psxRegs.cycle; - - for (i=0; i<3; i++) { - _rcntSet( i, 16 ); - } - for (i=3; i<6; i++) { - _rcntSet( i, 32 ); - } - - if(SPU2async) - { - u32 c = (psxCounters[6].CycleT - (psxRegs.cycle - psxCounters[6].sCycleT)) ; - if (c < psxNextCounter) { - psxNextCounter = c; - } - } - if(USBasync) - { - u32 c = (psxCounters[7].CycleT - (psxRegs.cycle - psxCounters[7].sCycleT)) ; - if (c < psxNextCounter) { - psxNextCounter = c; - } - } + if((u64)c < (u64)psxNextCounter) psxNextCounter = (u32)c; } @@ -135,94 +102,179 @@ void psxRcntInit() { psxCounters[4].interrupt = 0x08000; psxCounters[5].interrupt = 0x10000; - if (SPU2async != NULL) { - - - psxCounters[6].rate = 1; - psxCounters[6].CycleT = ((Config.Hacks & 0x4) ? 768 : 9216); + if (SPU2async != NULL) + { + psxCounters[6].rate = ((Config.Hacks & 0x4) ? 768 : (768*8)); + psxCounters[6].CycleT = psxCounters[6].rate; psxCounters[6].mode = 0x8; } - if (USBasync != NULL) { - psxCounters[7].rate = 1; - psxCounters[7].CycleT = PSXCLK/1000; + if (USBasync != NULL) + { + psxCounters[7].rate = PSXCLK/1000; + psxCounters[7].CycleT = psxCounters[7].rate; psxCounters[7].mode = 0x8; } - for (i=0; i<3; i++) - psxCounters[i].sCycleT = psxRegs.cycle; - for (i=3; i<6; i++) - psxCounters[i].sCycleT = psxRegs.cycle; - for (i=6; i<8; i++) + for (i=0; i<8; i++) psxCounters[i].sCycleT = psxRegs.cycle; - psxRcntSet(); + // Tell the IOP to branch ASAP, so that timers can get + // configured properly. + psxNextCounter = 1; + psxNextsCounter = psxRegs.cycle; } -void psxVBlankStart() { - cdvdVsync(); - psxHu32(0x1070)|= 1; - if(psxvblankgate & 1) psxCheckStartGate16(1); - if(psxvblankgate & (1 << 3)) psxCheckStartGate32(3); -} - -void psxVBlankEnd() { - psxHu32(0x1070)|= 0x800; - if(psxvblankgate & 1) psxCheckEndGate16(1); - if(psxvblankgate & (1 << 3)) psxCheckEndGate32(3); -} - -void psxCheckEndGate16(int i) +static void __fastcall _rcntTestTarget( int i ) { - //SysPrintf("End Gate %x\n", counter); - assert(i < 3); + if( psxCounters[i].count < psxCounters[i].target ) return; - if((psxCounters[i].mode & 0x1) == 0) return; //Ignore Gate + PSXCNT_LOG("IOP Counter[%d] target %x >= %x (mode: %x)\n", + i, psxCounters[i].count, psxCounters[i].target, psxCounters[i].mode); - switch((psxCounters[i].mode & 0x6) >> 1) { - case 0x0: //GATE_ON_count - psxCounters[i].count += (u16)psxRcntRcount16(i); //Only counts when signal is on - break; - case 0x1: //GATE_ON_ClearStart - if(psxCounters[i].mode & 0x10000000) psxRcntUpd16(i); - psxCounters[i].mode &= ~0x10000000; - break; - case 0x2: //GATE_ON_Clear_OFF_Start - psxCounters[i].mode &= ~0x10000000; - psxRcntUpd16(i); - break; - case 0x3: //GATE_ON_Start - break; - default: - SysPrintf("PCSX2 Warning: 16bit IOP Counter Gate Not Set!\n"); - break; + if (psxCounters[i].mode & IOPCNT_INT_TARGET) + { + // Target interrupt + + if(psxCounters[i].mode & 0x80) + psxCounters[i].mode &= ~0x0400; // Interrupt flag + psxCounters[i].mode |= 0x0800; // Target flag + + psxHu32(0x1070) |= psxCounters[i].interrupt; } + + if (psxCounters[i].mode & 0x08) + { + // Reset on target + psxCounters[i].count -= psxCounters[i].target; + if(!(psxCounters[i].mode & 0x40)) + { + SysPrintf("Counter %x repeat intr not set on zero ret, ignoring target\n", i); + psxCounters[i].target |= IOPCNT_FUTURE_TARGET; + } + } else psxCounters[i].target |= IOPCNT_FUTURE_TARGET; } -void psxCheckEndGate32(int i) + +static __forceinline void _rcntTestOverflow( int i ) { - assert(i >= 3); + u64 maxTarget = ( i < 3 ) ? 0xffff : 0xfffffffful; + if( psxCounters[i].count <= maxTarget ) return; - if((psxCounters[i].mode & 0x1) == 0) return; //Ignore Gate + PSXCNT_LOG("IOP Counter[%d] overflow 0x%x >= 0x%x (mode: %x)\n", + i, psxCounters[i].count, maxTarget, psxCounters[i].mode ); - switch((psxCounters[i].mode & 0x6) >> 1) { - case 0x0: //GATE_ON_count - psxCounters[i].count += (u32)psxRcntRcount32(i); //Only counts when signal is on - break; - case 0x1: //GATE_ON_ClearStart - if(psxCounters[i].mode & 0x10000000)psxRcntUpd32(i); - psxCounters[i].mode &= ~0x10000000; - break; - case 0x2: //GATE_ON_Clear_OFF_Start - psxCounters[i].mode &= ~0x10000000; - psxRcntUpd32(i); - break; - case 0x3: //GATE_ON_Start - break; - default: - SysPrintf("PCSX2 Warning: 32bit IOP Counter Gate Not Set!\n"); - break; + if(psxCounters[i].mode & IOPCNT_INT_OVERFLOW) + { + // Overflow interrupt + psxHu32(0x1070) |= psxCounters[i].interrupt; + psxCounters[i].mode |= 0x1000; // Overflow flag + if(psxCounters[i].mode & 0x80) + psxCounters[i].mode &= ~0x0400; // Interrupt flag } + + // Update count and target. + // Count wraps around back to zero, while the target is restored (if needed). + // (high bit of the target gets set by rcntWtarget when the target is behind + // the counter value, and thus should not be flagged until after an overflow) + + psxCounters[i].count &= maxTarget; + psxCounters[i].target &= maxTarget; +} + +/* +Gate: + TM_NO_GATE 000 + TM_GATE_ON_Count 001 + TM_GATE_ON_ClearStart 011 + TM_GATE_ON_Clear_OFF_Start 101 + TM_GATE_ON_Start 111 + + V-blank ----+ +----------------------------+ +------ + | | | | + | | | | + +----+ +----+ + TM_NO_GATE: + + 0================================>============ + + TM_GATE_ON_Count: + + <---->0==========================><---->0===== + + TM_GATE_ON_ClearStart: + + 0====>0================================>0===== + + TM_GATE_ON_Clear_OFF_Start: + + 0====><-------------------------->0====><----- + + TM_GATE_ON_Start: + + <---->0==========================>============ +*/ + +static void _psxCheckStartGate( int i ) +{ + if(!(psxCounters[i].mode & IOPCNT_ENABLE_GATE)) return; //Ignore Gate + + switch((psxCounters[i].mode & 0x6) >> 1) + { + case 0x0: //GATE_ON_count - stop count on gate start: + + // get the current count at the time of stoppage: + psxCounters[i].count = ( i < 3 ) ? + psxRcntRcount16( i ) : psxRcntRcount32( i ); + psxCounters[i].mode |= IOPCNT_GATE_STOPPED; + return; + + case 0x1: //GATE_ON_ClearStart - count normally with resets after every end gate + // do nothing - All counting will be done on a need-to-count basis. + return; + + case 0x2: //GATE_ON_Clear_OFF_Start - start counting on gate start, stop on gate end + psxCounters[i].count = 0; + psxCounters[i].sCycleT = psxRegs.cycle; + psxCounters[i].mode &= ~IOPCNT_GATE_STOPPED; + break; + + case 0x3: //GATE_ON_Start - start and count normally on gate end (no restarts or stops or clears) + // do nothing! + return; + } + _rcntSet( i ); +} + +static void _psxCheckEndGate(int i) +{ + if(!(psxCounters[i].mode & IOPCNT_ENABLE_GATE)) return; //Ignore Gate + + switch((psxCounters[i].mode & 0x6) >> 1) + { + case 0x0: //GATE_ON_count - reset and start counting + case 0x1: //GATE_ON_ClearStart - count normally with resets after every end gate + psxCounters[i].count = 0; + psxCounters[i].sCycleT = psxRegs.cycle; + psxCounters[i].mode &= ~IOPCNT_GATE_STOPPED; + break; + + case 0x2: //GATE_ON_Clear_OFF_Start - start counting on gate start, stop on gate end + psxCounters[i].count = ( i < 3 ) ? + psxRcntRcount16( i ) : psxRcntRcount32( i ); + psxCounters[i].mode |= IOPCNT_GATE_STOPPED; + return; // do not set the counter + + case 0x3: //GATE_ON_Start - start and count normally (no restarts or stops or clears) + if( psxCounters[i].mode & IOPCNT_GATE_STOPPED ) + { + psxCounters[i].count = 0; + psxCounters[i].sCycleT = psxRegs.cycle; + psxCounters[i].mode &= ~IOPCNT_GATE_STOPPED; + } + break; + } + _rcntSet( i ); } void psxCheckStartGate16(int i) @@ -233,561 +285,463 @@ void psxCheckStartGate16(int i) if(i == 0) { - if((psxCounters[1].mode & 0x101) == 0x100 || (psxCounters[1].mode & 0x10000101) == 0x101)psxCounters[1].count++; - if((psxCounters[3].mode & 0x101) == 0x100 || (psxCounters[3].mode & 0x10000101) == 0x101)psxCounters[3].count++; + // Alternate/scanline counters for Gates 1 and 3. + // We count them here so that they stay nicely synced with the EE's hsync. + + const u32 altSourceCheck = IOPCNT_ALT_SOURCE | IOPCNT_ENABLE_GATE; + const u32 stoppedGateCheck = (IOPCNT_GATE_STOPPED | altSourceCheck ); + + // count if alt source is enabled and either: + // * the gate is enabled and not stopped. + // * the gate is disabled. + + if( (psxCounters[1].mode & altSourceCheck) == IOPCNT_ALT_SOURCE || + (psxCounters[1].mode & stoppedGateCheck ) == altSourceCheck ) + { + psxCounters[1].count++; + _rcntTestTarget( 1 ); + _rcntTestOverflow( 1 ); + } + + if( (psxCounters[3].mode & altSourceCheck) == IOPCNT_ALT_SOURCE || + (psxCounters[3].mode & stoppedGateCheck ) == altSourceCheck ) + { + psxCounters[3].count++; + _rcntTestTarget( 3 ); + _rcntTestOverflow( 3 ); + } } - if((psxCounters[i].mode & 0x1) == 0) return; //Ignore Gate - - //SysPrintf("PSX Gate %x\n", i); - switch((psxCounters[i].mode & 0x6) >> 1) - { - case 0x0: //GATE_ON_count - psxRcntUpd32(i); - psxCounters[i].mode |= 0x10000000; - break; - case 0x1: //GATE_ON_ClearStart - if(psxCounters[i].mode & 0x10000000)psxRcntUpd16(i); - psxCounters[i].mode &= ~0x10000000; - break; - case 0x2: //GATE_ON_Clear_OFF_Start - psxRcntReset32(i); - psxCounters[i].mode |= 0x10000000; - break; - case 0x3: //GATE_ON_Start - psxCounters[i].mode &= ~0x10000000; - break; - default: - SysPrintf("PCSX2 Warning: 16bit IOP Counter Gate Not Set!\n"); - break; - } + _psxCheckStartGate( i ); } -void psxCheckStartGate32(int i) +void psxCheckEndGate16(int i) { - assert(i >= 3); - - if((psxCounters[i].mode & 0x1) == 0) return; //Ignore Gate - - //SysPrintf("PSX Gate %x\n", i); - switch((psxCounters[i].mode & 0x6) >> 1) { - case 0x0: //GATE_ON_count - psxRcntUpd32(i); - psxCounters[i].mode &= ~0x10000000; - break; - case 0x1: //GATE_ON_ClearStart - if(psxCounters[i].mode & 0x10000000)psxRcntUpd32(i); - psxCounters[i].mode &= ~0x10000000; - break; - case 0x2: //GATE_ON_Clear_OFF_Start - psxRcntReset32(i); - psxCounters[i].mode |= 0x10000000; - break; - case 0x3: //GATE_ON_Start - psxCounters[i].mode &= ~0x10000000; - break; - default: - SysPrintf("PCSX2 Warning: 32bit IOP Counter Gate Not Set!\n"); - break; - } + assert(i < 3); + _psxCheckEndGate( i ); } -void _testRcnt16target(int i) { +static void psxCheckStartGate32(int i) +{ + // 32 it gate is called for gate 3 only. Ever. + assert(i == 3); - PSXCNT_LOG("[%d] target %x >= %x (CycleT); count=%I64x, target=%I64x, mode=%I64x\n", i, (psxRegs.cycle - psxCounters[i].sCycleT), psxCounters[i].CycleT, psxCounters[i].count, psxCounters[i].target, psxCounters[i].mode); - - if(psxCounters[i].target > 0xffff) { - psxCounters[i].target &= 0xffff; - //SysPrintf("IOP 16 Correcting target target %x\n", psxCounters[i].target); - } - - if (psxCounters[i].mode & 0x10){ - if(psxCounters[i].mode & 0x80)psxCounters[i].mode&= ~0x0400; // Interrupt flag - psxCounters[i].mode|= 0x0800; // Target flag - } - - - - if (psxCounters[i].mode & 0x10) { // Target interrupt - psxHu32(0x1070)|= psxCounters[i].interrupt; - } - - if (psxCounters[i].mode & 0x08) { // Reset on target - psxCounters[i].count -= psxCounters[i].target; - if((psxCounters[i].mode & 0x40) == 0){ - SysPrintf("Counter %x repeat intr not set on zero ret, ignoring target\n", i); - psxCounters[i].target += 0x1000000000ULL; - } - - } else psxCounters[i].target += 0x1000000000ULL; - + _psxCheckStartGate( i ); } -void _testRcnt16overflow(int i) { - - PSXCNT_LOG("[%d] overflow 0x%x >= 0x%x (Cycle); Rcount=0x%x, count=0x%x\n", i, (psxRegs.cycle - psxCounters[i].sCycle) / psxCounters[i].rate, psxCounters[i].Cycle, psxRcntRcount16(i), psxCounters[i].count); - - if (psxCounters[i].mode & 0x0020) { // Overflow interrupt - psxHu32(0x1070)|= psxCounters[i].interrupt; - psxCounters[i].mode|= 0x1000; // Overflow flag - if(psxCounters[i].mode & 0x80){ - - psxCounters[i].mode&= ~0x0400; // Interrupt flag - } - //SysPrintf("Overflow 16\n"); - } - psxCounters[i].count -= 0x10000; - psxRcntUpd16(i); - if(psxCounters[i].target > 0xffff) { - if((psxCounters[i].mode & 0x50) <= 0x40 && (psxCounters[i].mode & 0x50) != 0) SysPrintf("Counter %x overflowing, no repeat interrupt mode = %x\n", i, psxCounters[i].mode); - psxCounters[i].target &= 0xffff; - //SysPrintf("IOP 16 Correcting target ovf %x\n", psxCounters[i].target); - } -} - -void _testRcnt32target(int i) { - - PSXCNT_LOG("[%d] target %x >= %x (CycleT); count=%I64x, target=%I64x, mode=%I64x\n", i, (psxRegs.cycle - psxCounters[i].sCycleT), psxCounters[i].CycleT, psxCounters[i].count, psxCounters[i].target, psxCounters[i].mode); - if(psxCounters[i].target > 0xffffffff) { - //SysPrintf("IOP 32 Correcting target on target reset\n"); - psxCounters[i].target &= 0xffffffff; - } - - -// newtarget[i] = 0; - - - if (psxCounters[i].mode & 0x10){ - if(psxCounters[i].mode & 0x80)psxCounters[i].mode&= ~0x0400; // Interrupt flag - psxCounters[i].mode|= 0x0800; // Target flag - } - - - if (psxCounters[i].mode & 0x10) { // Target interrupt - psxHu32(0x1070)|= psxCounters[i].interrupt; - - } - - if (psxCounters[i].mode & 0x08) { // Reset on target - psxCounters[i].count -= psxCounters[i].target; - if((psxCounters[i].mode & 0x40) == 0){ - SysPrintf("Counter %x repeat intr not set on zero ret, ignoring target\n", i); - psxCounters[i].target += 0x1000000000ULL; - } - - } else psxCounters[i].target += 0x1000000000ULL; - -} - -void _testRcnt32overflow(int i) { - - PSXCNT_LOG("[%d] overflow 0x%x >= 0x%x (Cycle); Rcount=0x%x, count=0x%x\n", i, (psxRegs.cycle - psxCounters[i].sCycle), psxCounters[i].Cycle, psxRcntRcount32(i), psxCounters[i].count); - //SysPrintf("Overflow 32\n"); - if (psxCounters[i].mode & 0x0020) { // Overflow interrupt - psxHu32(0x1070)|= psxCounters[i].interrupt; - psxCounters[i].mode|= 0x1000; // Overflow flag - if(psxCounters[i].mode & 0x80){ - - psxCounters[i].mode&= ~0x0400; // Interrupt flag - } - } - psxCounters[i].count -= 0x100000000ULL; - if(psxCounters[i].target > 0xffffffff) - { - //SysPrintf("IOP 32 Correcting target on overflow\n"); - if((psxCounters[i].mode & 0x50) <= 0x40 && (psxCounters[i].mode & 0x50) != 0) - SysPrintf("Counter %x overflowing, no repeat interrupt mode = %x\n", i, psxCounters[i].mode); - psxCounters[i].target &= 0xffffffff; - } +static void psxCheckEndGate32(int i) +{ + assert(i == 3); + _psxCheckEndGate( i ); } -void _testRcnt16(int i) { - - if (/*psxCounters[i].target > 0 &&*/ (s64)(psxCounters[i].target - psxCounters[i].count) <= 0){ - _testRcnt16target(i); - } - if (psxCounters[i].count > 0xffff) - _testRcnt16overflow(i); +void psxVBlankStart() +{ + cdvdVsync(); + psxHu32(0x1070) |= 1; + if(psxvblankgate & 1) psxCheckStartGate16(1); + if(psxvblankgate & (1 << 3)) psxCheckStartGate32(3); } -void _testRcnt32(int i) { - - if (/*psxCounters[i].target > 0 && */(s64)(psxCounters[i].target - psxCounters[i].count) <= 0){ - _testRcnt32target(i); - } - if (psxCounters[i].count > 0xffffffff) - _testRcnt32overflow(i); - - +void psxVBlankEnd() +{ + psxHu32(0x1070) |= 0x800; + if(psxvblankgate & 1) psxCheckEndGate16(1); + if(psxvblankgate & (1 << 3)) psxCheckEndGate32(3); } -void psxRcntUpdate() { + +void psxRcntUpdate() +{ int i; - u32 change = 0; + //u32 change = 0; - for (i=0; i<=5; i++) { - if((psxCounters[i].mode & 0x1) != 0 || psxCounters[i].rate == PSXHBLANK){ - //SysPrintf("Stopped accidental update of psx counter %x when using a gate\hblank source\n", i); - continue; - } - - change = psxRegs.cycle - psxCounters[i].sCycleT; - psxCounters[i].count += change / psxCounters[i].rate; - if(psxCounters[i].rate != 1){ - change -= (change / psxCounters[i].rate) * psxCounters[i].rate; - psxCounters[i].sCycleT = psxRegs.cycle - change; - //if(change > 0) SysPrintf("PSX Change saved on %x = %x\n", i, change); - } else psxCounters[i].sCycleT = psxRegs.cycle; + for (i=0; i<=5; i++) + { + s32 change = psxRegs.cycle - psxCounters[i].sCycleT; + + // don't count disabled, gated, or hblank counters... + // We can't check the ALTSOURCE flag because the PSXCLOCK source *should* + // be counted here. + + if( psxCounters[i].mode & (IOPCNT_GATE_STOPPED | IOPCNT_ENABLE_GATE) ) continue; + if( psxCounters[i].rate == PSXHBLANK ) continue; + if( change <= 0 ) continue; + + psxCounters[i].count += change / psxCounters[i].rate; + if(psxCounters[i].rate != 1) + { + change -= (change / psxCounters[i].rate) * psxCounters[i].rate; + psxCounters[i].sCycleT = psxRegs.cycle - change; + } + else + psxCounters[i].sCycleT = psxRegs.cycle; } - _testRcnt16(0); - _testRcnt16(1); - _testRcnt16(2); - _testRcnt32(3); - _testRcnt32(4); - _testRcnt32(5); + // Do target/overflow testing + // Optimization Note: This approach is very sound. Please do not try to unroll it + // as the size of the Test functions will cause code cache clutter and slowness. + + for( i=0; i<6; i++ ) + { + // don't do target/oveflow checks for hblankers. Those + // checks are done when the counters are updated. + if( psxCounters[i].rate == PSXHBLANK ) continue; + if( psxCounters[i].mode & IOPCNT_GATE_STOPPED ) continue; + _rcntTestTarget( i ); + _rcntTestOverflow( i ); + + // perform second target test because if we overflowed above it's possible we + // already shot past our target if it was very near zero. + + //if( psxCounters[i].count >= psxCounters[i].target ) _rcntTestTarget( i ); + } + + psxNextCounter = 0xffffff; + psxNextsCounter = psxRegs.cycle; if(SPU2async) { - if((psxRegs.cycle - psxCounters[6].sCycleT) >= psxCounters[6].CycleT) + const s32 difference = psxRegs.cycle - psxCounters[6].sCycleT; + s32 c = psxCounters[6].CycleT; + + if(difference >= psxCounters[6].CycleT) { - SPU2async(psxRegs.cycle - psxCounters[6].sCycleT); - //SysPrintf("cycles sent to SPU2 %x\n", psxRegs.cycle - psxCounters[6].sCycleT); + SPU2async(difference); psxCounters[6].sCycleT = psxRegs.cycle; - psxCounters[6].CycleT = ((Config.Hacks & 0x4) ? 768 : 9216); + psxCounters[6].CycleT = psxCounters[6].rate; } + else c -= difference; + psxNextCounter = c; } if(USBasync) { - if ((psxRegs.cycle - psxCounters[7].sCycleT) >= psxCounters[7].CycleT) { - USBasync(psxRegs.cycle - psxCounters[7].sCycleT); + const s32 difference = psxRegs.cycle - psxCounters[7].sCycleT; + s32 c = psxCounters[7].CycleT; + + if(difference >= psxCounters[7].CycleT) + { + USBasync(difference); psxCounters[7].sCycleT = psxRegs.cycle; + psxCounters[7].CycleT = psxCounters[7].rate; } + else c -= difference; + if (c < psxNextCounter) psxNextCounter = c; } - psxRcntSet(); + for (i=0; i<6; i++) _rcntSet( i ); } -void psxRcntWcount16(int index, u32 value) { - u32 change = 0; - PSXCNT_LOG("writeCcount[%d] = %x\n", index, value); +void psxRcntWcount16(int index, u32 value) +{ + u32 change; - change = psxRegs.cycle - psxCounters[index].sCycleT; - //psxCounters[i].count += change / psxCounters[i].rate; - if(psxCounters[index].rate != 1){ - change -= (change / psxCounters[index].rate) * psxCounters[index].rate; - psxCounters[index].sCycleT = psxRegs.cycle - change; - if(change > 0) SysPrintf("PSX Change count write 16 %x = %x\n", index, change); - } - psxCounters[index].count = value & 0xffff; - if(psxCounters[index].target > 0xffff) { - //SysPrintf("IOP 16 Correcting target on count write\n"); - psxCounters[index].target &= 0xffff; - } - if(psxCounters[index].rate == PSXHBLANK)SysPrintf("Whoops 16 IOP\n"); - psxRcntUpd16(index); - _rcntSet( index, 16 ); -} + assert( index < 3 ); + PSXCNT_LOG("IOP Counter[%d] > writeCount16 = %x\n", index, value); -void psxRcntWcount32(int index, u32 value) { - u32 change = 0; - PSXCNT_LOG("writeCcount[%d] = %x\n", index, value); + if(psxCounters[index].rate != PSXHBLANK) + { + // Re-adjust the sCycleT to match where the counter is currently + // (remainder of the rate divided into the time passed will do the trick) change = psxRegs.cycle - psxCounters[index].sCycleT; - //psxCounters[i].count += change / psxCounters[i].rate; - if(psxCounters[index].rate != 1){ - change -= (change / psxCounters[index].rate) * psxCounters[index].rate; - psxCounters[index].sCycleT = psxRegs.cycle - change; - if(change > 0) SysPrintf("PSX Change count write 32 %x = %x\n", index, change); - } - psxCounters[index].count = value; - if(psxCounters[index].target > 0xffffffff) { - //SysPrintf("IOP 32 Correcting target\n"); - psxCounters[index].target &= 0xffffffff; - } - if(psxCounters[index].rate == PSXHBLANK)SysPrintf("Whoops 32 IOP\n"); - //psxRcntUpd32(index); - _rcntSet( index, 32 ); + psxCounters[index].sCycleT = psxRegs.cycle - (change % psxCounters[index].rate); + } + + psxCounters[index].count = value & 0xffff; + psxCounters[index].target &= 0xffff; + _rcntSet( index ); } -void psxRcnt0Wmode(u32 value) { - PSXCNT_LOG("IOP writeCmode[0] = %lx\n", value); +void psxRcntWcount32(int index, u32 value) +{ + u32 change; -#if 0 - if (value & 0x1c00) { - SysPrintf("Counter 0 Value write %x\n", value & 0x1c00); + assert( index >= 3 && index < 6 ); + PSXCNT_LOG("IOP Counter[%d] > writeCount32 = %x\n", index, value); + + if(psxCounters[index].rate != PSXHBLANK) + { + // Re-adjust the sCycleT to match where the counter is currently + // (remainder of the rate divided into the time passed will do the trick) + + change = psxRegs.cycle - psxCounters[index].sCycleT; + psxCounters[index].sCycleT = psxRegs.cycle - (change % psxCounters[index].rate); } -#endif + + psxCounters[index].count = value & 0xffffffff; + psxCounters[index].target &= 0xffffffff; + _rcntSet( index ); +} + +void psxRcnt0Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[0] > writeMode = %lx\n", value); psxCounters[0].mode = value; psxCounters[0].mode|= 0x0400; psxCounters[0].rate = 1; - if(value & 0x100) { - //SysPrintf("Timer 0 Set to Pixel clock %x\n", value); + if(value & IOPCNT_ALT_SOURCE) psxCounters[0].rate = PSXPIXEL; - }// else SysPrintf("Timer 0 Set to 1 %x\n", value); - if(psxCounters[0].mode & 0x1){ - SysPrintf("Gate Check set on Counter 0 %x\n", value); - psxCounters[0].mode|= 0x1000000; + if(psxCounters[0].mode & IOPCNT_ENABLE_GATE) + { + // gated counters are added up as per the h/vblank timers. + PSXCNT_LOG("IOP Counter[0] > Gate Check set, value = %x\n", value); psxhblankgate |= 1; - }else - psxhblankgate &= ~1; + } + else psxhblankgate &= ~1; psxCounters[0].count = 0; - psxRcntUpd16(0); - if(psxCounters[0].target > 0xffff) { - //SysPrintf("IOP 16 Correcting target 0 after mode write\n"); - psxCounters[0].target &= 0xffff; - } - _rcntSet( 0, 16 ); - //} + psxCounters[0].sCycleT = psxRegs.cycle; + psxCounters[0].target &= 0xffff; + + _rcntSet( 0 ); } -void psxRcnt1Wmode(u32 value) { - PSXCNT_LOG("IOP writeCmode[1] = %lx\n", value); - - if (value & 0x1c00) { - SysPrintf("Counter 1 Value write %x\n", value & 0x1c00); - } +void psxRcnt1Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[0] > writeMode = %lx\n", value); psxCounters[1].mode = value; psxCounters[1].mode|= 0x0400; psxCounters[1].rate = 1; - if(value & 0x100){ - //SysPrintf("Timer 1 Set to HBlank clock %x\n", value); + if(value & IOPCNT_ALT_SOURCE) psxCounters[1].rate = PSXHBLANK; - }// else SysPrintf("Timer 1 Set to 1 clock %x\n", value); - if(psxCounters[1].mode & 0x1){ - SysPrintf("Gate Check set on Counter 1 %x\n", value); - psxCounters[1].mode|= 0x1000000; + if(psxCounters[1].mode & IOPCNT_ENABLE_GATE) + { + PSXCNT_LOG("IOP Counter[1] > Gate Check set, value = %x\n", value); psxvblankgate |= 1<<1; - }else - psxvblankgate &= ~(1<<1); + } + else psxvblankgate &= ~(1<<1); psxCounters[1].count = 0; - psxRcntUpd16(1); - if(psxCounters[1].target > 0xffff) { - //SysPrintf("IOP 16 Correcting target 1 after mode write\n"); - psxCounters[1].target &= 0xffff; - } - _rcntSet( 1, 16 ); - //} + psxCounters[1].sCycleT = psxRegs.cycle; + psxCounters[1].target &= 0xffff; + _rcntSet( 1 ); } -void psxRcnt2Wmode(u32 value) { - PSXCNT_LOG("IOP writeCmode[2] = %lx\n", value); - - if (value & 0x1c00) { - SysPrintf("Counter 2 Value write %x\n", value & 0x1c00); - } +void psxRcnt2Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[0] > writeMode = %lx\n", value); psxCounters[2].mode = value; psxCounters[2].mode|= 0x0400; - switch(value & 0x200){ - case 0x200: - //SysPrintf("Timer 2 Set to 8 %x\n", value); - psxCounters[2].rate = 8; - break; - case 0x000: - //SysPrintf("Timer 2 Set to 1 %x\n", value); - psxCounters[2].rate = 1; - break; + switch(value & 0x200) + { + case 0x200: psxCounters[2].rate = 8; break; + case 0x000: psxCounters[2].rate = 1; break; } - if((psxCounters[2].mode & 0x7) == 0x7 || (psxCounters[2].mode & 0x7) == 0x1){ + if((psxCounters[2].mode & 0x7) == 0x7 || (psxCounters[2].mode & 0x7) == 0x1) + { //SysPrintf("Gate set on IOP C2, disabling\n"); - psxCounters[2].mode|= 0x1000000; + psxCounters[2].mode |= IOPCNT_GATE_STOPPED; } - // Need to set a rate and target + psxCounters[2].count = 0; - psxRcntUpd16(2); - if(psxCounters[2].target > 0xffff) { - //SysPrintf("IOP 16 Correcting target 2 after mode write\n"); - psxCounters[2].target &= 0xffff; - } - _rcntSet( 2, 16 ); + psxCounters[2].sCycleT = psxRegs.cycle; + psxCounters[2].target &= 0xffff; + _rcntSet( 2 ); } -void psxRcnt3Wmode(u32 value) { - PSXCNT_LOG("IOP writeCmode[3] = %lx\n", value); - - if (value & 0x1c00) { - SysPrintf("Counter 3 Value write %x\n", value & 0x1c00); - } +void psxRcnt3Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[3] > writeMode = %lx\n", value); psxCounters[3].mode = value; psxCounters[3].rate = 1; psxCounters[3].mode|= 0x0400; - if(value & 0x100){ - //SysPrintf("Timer 3 Set to HBLANK clock %x\n", value); + if(value & IOPCNT_ALT_SOURCE) psxCounters[3].rate = PSXHBLANK; - }//else SysPrintf("Timer 3 Set to 1 %x\n", value); - if(psxCounters[3].mode & 0x1){ - SysPrintf("Gate Check set on Counter 3\n"); - psxCounters[3].mode|= 0x1000000; + if(psxCounters[3].mode & IOPCNT_ENABLE_GATE) + { + PSXCNT_LOG("IOP Counter[3] > Gate Check set, value = %x\n", value); psxvblankgate |= 1<<3; - }else - psxvblankgate &= ~(1<<3); + } + else psxvblankgate &= ~(1<<3); psxCounters[3].count = 0; - psxRcntUpd32(3); - if(psxCounters[3].target > 0xffffffff) { - //SysPrintf("IOP 32 Correcting target 3 after mode write\n"); - psxCounters[3].target &= 0xffffffff; - } - _rcntSet( 3, 32 ); - //} + psxCounters[3].sCycleT = psxRegs.cycle; + psxCounters[3].target &= 0xffffffff; + _rcntSet( 3 ); } -void psxRcnt4Wmode(u32 value) { - PSXCNT_LOG("IOP writeCmode[4] = %lx\n", value); - - if (value & 0x1c00) { - SysPrintf("Counter 4 Value write %x\n", value & 0x1c00); - } +void psxRcnt4Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[4] > writeMode = %lx\n", value); psxCounters[4].mode = value; psxCounters[4].mode|= 0x0400; - switch(value & 0x6000){ - case 0x0000: - //SysPrintf("Timer 4 Set to 1 %x\n", value); - psxCounters[4].rate = 1; - break; - case 0x2000: - SysPrintf("Timer 4 Set to 8 %x\n", value); - psxCounters[4].rate = 8; - break; - case 0x4000: - SysPrintf("Timer 4 Set to 16 %x\n", value); - psxCounters[4].rate = 16; - break; - case 0x6000: - SysPrintf("Timer 4 Set to 256 %x\n", value); - psxCounters[4].rate = 256; - break; + switch(value & 0x6000) + { + case 0x0000: psxCounters[4].rate = 1; break; + case 0x2000: psxCounters[4].rate = 8; break; + case 0x4000: psxCounters[4].rate = 16; break; + case 0x6000: psxCounters[4].rate = 256; break; } // Need to set a rate and target - if((psxCounters[4].mode & 0x7) == 0x7 || (psxCounters[4].mode & 0x7) == 0x1){ + if((psxCounters[4].mode & 0x7) == 0x7 || (psxCounters[4].mode & 0x7) == 0x1) + { SysPrintf("Gate set on IOP C4, disabling\n"); - psxCounters[4].mode|= 0x1000000; + psxCounters[4].mode |= IOPCNT_GATE_STOPPED; } + psxCounters[4].count = 0; - psxRcntUpd32(4); - if(psxCounters[4].target > 0xffffffff) { - //SysPrintf("IOP 32 Correcting target 4 after mode write\n"); - psxCounters[4].target &= 0xffffffff; - } - _rcntSet( 4, 32 ); + psxCounters[4].sCycleT = psxRegs.cycle; + psxCounters[4].target &= 0xffffffff; + _rcntSet( 4 ); } -void psxRcnt5Wmode(u32 value) { - PSXCNT_LOG("IOP writeCmode[5] = %lx\n", value); - - if (value & 0x1c00) { - SysPrintf("Counter 5 Value write %x\n", value & 0x1c00); - } +void psxRcnt5Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[5] > writeMode = %lx\n", value); psxCounters[5].mode = value; psxCounters[5].mode|= 0x0400; - switch(value & 0x6000){ - case 0x0000: - //SysPrintf("Timer 5 Set to 1 %x\n", value); - psxCounters[5].rate = 1; - break; - case 0x2000: - SysPrintf("Timer 5 Set to 8 %x\n", value); - psxCounters[5].rate = 8; - break; - case 0x4000: - SysPrintf("Timer 5 Set to 16 %x\n", value); - psxCounters[5].rate = 16; - break; - case 0x6000: - SysPrintf("Timer 5 Set to 256 %x\n", value); - psxCounters[5].rate = 256; - break; + switch(value & 0x6000) + { + case 0x0000: psxCounters[5].rate = 1; break; + case 0x2000: psxCounters[5].rate = 8; break; + case 0x4000: psxCounters[5].rate = 16; break; + case 0x6000: psxCounters[5].rate = 256; break; } // Need to set a rate and target - if((psxCounters[5].mode & 0x7) == 0x7 || (psxCounters[5].mode & 0x7) == 0x1){ + if((psxCounters[5].mode & 0x7) == 0x7 || (psxCounters[5].mode & 0x7) == 0x1) + { SysPrintf("Gate set on IOP C5, disabling\n"); - psxCounters[5].mode|= 0x1000000; + psxCounters[5].mode |= IOPCNT_GATE_STOPPED; } + psxCounters[5].count = 0; - psxRcntUpd32(5); - if(psxCounters[5].target > 0xffffffff) { - //SysPrintf("IOP 32 Correcting target 5 after mode write\n"); - psxCounters[5].target &= 0xffffffff; - } - _rcntSet( 5, 32 ); + psxCounters[5].sCycleT = psxRegs.cycle; + psxCounters[5].target &= 0xffffffff; + _rcntSet( 5 ); } -void psxRcntWtarget16(int index, u32 value) { - PSXCNT_LOG("writeCtarget16[%ld] = %lx\n", index, value); +void psxRcntWtarget16(int index, u32 value) +{ + assert( index < 3 ); + PSXCNT_LOG("IOP Counter[%d] > writeTarget16 = %lx\n", index, value); psxCounters[index].target = value & 0xffff; - if(psxCounters[index].target <= psxRcntCycles(index)/* && psxCounters[index].target != 0*/) { - //SysPrintf("IOP 16 Saving %x target from early trigger target = %x, count = %I64x\n", index, psxCounters[index].target, psxRcntCycles(index)); - psxCounters[index].target += 0x1000000000ULL; - } - _rcntSet( index, 16 ); + // protect the target from an early arrival. + // if the target is behind the current count, then set the target overflow + // flag, so that the target won't be active until after the next overflow. + + if(psxCounters[index].target <= psxRcntCycles(index)) + psxCounters[index].target |= IOPCNT_FUTURE_TARGET; + + _rcntSet( index ); } -void psxRcntWtarget32(int index, u32 value) { - PSXCNT_LOG("writeCtarget32[%ld] = %lx (count=%lx) ; sCycleT: %x CycleT: %x psxRegscycle %x\n", - index, value, psxCounters[index].count, psxCounters[index].sCycleT, psxCounters[index].CycleT, psxRegs.cycle); +void psxRcntWtarget32(int index, u32 value) +{ + assert( index >= 3 && index < 6); + PSXCNT_LOG("IOP Counter[%d] > writeTarget32 = %lx\n", index, value); psxCounters[index].target = value; - if(psxCounters[index].target <= psxRcntCycles(index)/* && psxCounters[index].target != 0*/) { - //SysPrintf("IOP 32 Saving %x target from early trigger target = %x, count = %I64x\n", index, psxCounters[index].target, psxRcntCycles(index)); - psxCounters[index].target += 0x1000000000ULL; + + // protect the target from an early arrival. + // if the target is behind the current count, then set the target overflow + // flag, so that the target won't be active until after the next overflow. + + if(psxCounters[index].target <= psxRcntCycles(index)) + psxCounters[index].target |= IOPCNT_FUTURE_TARGET; + + _rcntSet( index ); +} + +u16 psxRcntRcount16(int index) +{ + u32 retval = (u32)psxCounters[index].count; + + assert( index < 3 ); + + PSXCNT_LOG("IOP Counter[%d] > readCount16 = %lx", index, (u16)retval ); + + // Don't count HBLANK timers + // Don't count stopped gates either. + + if( !( psxCounters[index].mode & IOPCNT_GATE_STOPPED ) && + ( psxCounters[index].rate != PSXHBLANK ) ) + { + u32 delta = (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate); + retval += delta; + PSXCNT_LOG(" (delta = %lx)", delta ); } + PSXCNT_LOG( "\n" ); - _rcntSet( index, 32 ); + return (u16)retval; } -u16 psxRcntRcount16(int index) { - if(psxCounters[index].mode & 0x1000000) return (u16)psxCounters[index].count; - return (u16)(psxCounters[index].count + (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate)); +u32 psxRcntRcount32(int index) +{ + u32 retval = (u32)psxCounters[index].count; + + assert( index >= 3 && index < 6 ); + + PSXCNT_LOG("IOP Counter[%d] > readCount32 = %lx", index, retval ); + + if( !( psxCounters[index].mode & IOPCNT_GATE_STOPPED ) && + ( psxCounters[index].rate != PSXHBLANK ) ) + { + u32 delta = (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate); + retval += delta; + PSXCNT_LOG(" (delta = %lx)", delta ); + } + PSXCNT_LOG( "\n" ); + + return retval; } -u32 psxRcntRcount32(int index) { - if(psxCounters[index].mode & 0x1000000) return (u32)psxCounters[index].count; - return (u32)(psxCounters[index].count + (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate)); -} - -u64 psxRcntCycles(int index) { - if(psxCounters[index].mode & 0x1000000) return psxCounters[index].count; +u64 psxRcntCycles(int index) +{ + if(psxCounters[index].mode & IOPCNT_GATE_STOPPED || psxCounters[index].rate == PSXHBLANK ) return psxCounters[index].count; return (u64)(psxCounters[index].count + (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate)); } extern u32 dwCurSaveStateVer; int psxRcntFreeze(gzFile f, int Mode) { - if( Mode == 0 && (dwCurSaveStateVer < 0x7a300010) ) { // reading - // struct used to be 32bit count and target - int i; - u32 val; - for(i = 0; i < ARRAYSIZE(psxCounters); ++i) { - gzfreeze(&val,4); psxCounters[i].count = val; - gzfreeze(&val,4); psxCounters[i].mode = val; - gzfreeze(&val,4); psxCounters[i].target = val; - gzfreeze((u8*)&psxCounters[i].rate, sizeof(psxCounters[i])-20); - } - } + if( Mode == 0 && (dwCurSaveStateVer < 0x7a300010) ) + { + // --- Reading Mode --- + // struct used to be 32bit count and target + int i; + u32 val; + for(i = 0; i < ARRAYSIZE(psxCounters); ++i) + { + gzfreeze(&val,4); psxCounters[i].count = val; + gzfreeze(&val,4); psxCounters[i].mode = val; + gzfreeze(&val,4); psxCounters[i].target = val; + gzfreeze((u8*)&psxCounters[i].rate, sizeof(psxCounters[i])-20); + } + } else + { gzfreezel(psxCounters); + if( Mode == 0 ) + { + // This is needed to make old save states compatible. + psxCounters[6].rate = ((Config.Hacks & 0x4) ? 768 : (768*8)); + psxCounters[6].CycleT = psxCounters[6].rate; + psxCounters[7].rate = PSXCLK/1000; + psxCounters[7].CycleT = psxCounters[7].rate; + + } + } return 0; } diff --git a/pcsx2/PsxCounters.h b/pcsx2/PsxCounters.h index f26a832d8b..4625f2c0bc 100644 --- a/pcsx2/PsxCounters.h +++ b/pcsx2/PsxCounters.h @@ -19,16 +19,21 @@ #ifndef __PSXCOUNTERS_H__ #define __PSXCOUNTERS_H__ +// fixme: sCycle and Cycle are unused. +// Can't remove them without making a new savestate version though. + typedef struct { u64 count, target; u32 mode; u32 rate, interrupt, otarget; u32 sCycle, Cycle; - u32 sCycleT, CycleT; + u32 sCycleT; + s32 CycleT; } psxCounter; extern psxCounter psxCounters[8]; -extern u32 psxNextCounter, psxNextsCounter; +extern s32 psxNextCounter; +extern u32 psxNextsCounter; void psxRcntInit(); void psxRcntUpdate(); diff --git a/pcsx2/PsxHw.h b/pcsx2/PsxHw.h index 92ddf83113..894d12e19b 100644 --- a/pcsx2/PsxHw.h +++ b/pcsx2/PsxHw.h @@ -78,7 +78,7 @@ #define HW_DMA_PCR2 (psxHu32(0x1570)) #define HW_DMA_ICR2 (psxHu32(0x1574)) -extern void PSX_INT( int n, u32 ecycle); +extern void PSX_INT( int n, s32 ecycle); void psxHwReset(); u8 psxHwRead8 (u32 add); diff --git a/pcsx2/PsxInterpreter.c b/pcsx2/PsxInterpreter.c index e9e0dc5e12..2ecae56394 100644 --- a/pcsx2/PsxInterpreter.c +++ b/pcsx2/PsxInterpreter.c @@ -385,7 +385,7 @@ void spyFunctions(){ * Format: OP rt, rs, immediate * *********************************************************/ void psxADDI() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) + _Imm_ ; } // Rt = Rs + Im (Exception on Integer Overflow) -void psxADDIU() { if (!_Rt_) { g_eeTightenSync+=3; zeroEx(); return; } _rRt_ = _u32(_rRs_) + _Imm_ ; } // Rt = Rs + Im +void psxADDIU() { if (!_Rt_) { g_psxNextBranchCycle=psxRegs.cycle; zeroEx(); return; } _rRt_ = _u32(_rRs_) + _Imm_ ; } // Rt = Rs + Im void psxANDI() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) & _ImmU_; } // Rt = Rs And Im void psxORI() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) | _ImmU_; } // Rt = Rs Or Im void psxXORI() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) ^ _ImmU_; } // Rt = Rs Xor Im diff --git a/pcsx2/R3000A.c b/pcsx2/R3000A.c index ff7320d51f..04de1a9bb8 100644 --- a/pcsx2/R3000A.c +++ b/pcsx2/R3000A.c @@ -23,10 +23,13 @@ #include "PsxCommon.h" #include "Misc.h" -// used for constant propagation R3000Acpu *psxCpu; + +// used for constant propagation u32 g_psxConstRegs[32]; u32 g_psxHasConstReg, g_psxFlushedConstReg; + +// Controls when branch tests are performed. u32 g_psxNextBranchCycle = 0; // This value is used when the IOP execution is broken to return contorl to the EE. @@ -39,6 +42,8 @@ s32 psxBreak = 0; // control is returned to the EE. s32 psxCycleEE = -1; +int iopBranchAction = 0; + PCSX2_ALIGNED16(psxRegisters psxRegs); @@ -46,6 +51,10 @@ int psxInit() { psxCpu = CHECK_EEREC ? &psxRec : &psxInt; + g_psxNextBranchCycle = 8; + psxBreak = 0; + psxCycleEE = -1; + #ifdef PCSX2_DEVBUILD Log=0; #endif @@ -144,52 +153,68 @@ void psxException(u32 code, u32 bd) { }*/ } -__forceinline void PSX_INT( int n, u32 ecycle ) +__forceinline void psxSetNextBranch( u32 startCycle, s32 delta ) +{ + //assert( startCycle <= psxRegs.cycle ); + // typecast the conditional to signed so that things don't blow up + // if startCycle is greater than our next branch cycle. + + if( (int)(g_psxNextBranchCycle - startCycle) > delta ) + g_psxNextBranchCycle = startCycle + delta; +} + +__forceinline void psxSetNextBranchDelta( s32 delta ) +{ + psxSetNextBranch( psxRegs.cycle, delta ); +} + +__forceinline int psxTestCycle( u32 startCycle, s32 delta ) +{ + // typecast the conditional to signed so that things don't explode + // if the startCycle is ahead of our current cpu cycle. + + return (int)(psxRegs.cycle - startCycle) >= delta; +} + +__forceinline void PSX_INT( int n, s32 ecycle ) { psxRegs.interrupt |= 1 << n; psxRegs.sCycle[n] = psxRegs.cycle; psxRegs.eCycle[n] = ecycle; - if( (g_psxNextBranchCycle - psxRegs.sCycle[n]) <= psxRegs.eCycle[n] ) return; + // Interrupt is happening soon: make sure everyone is aware (including the EE!) - // Interrupt is happening soon: make sure everyone is aware (includeing the EE!) - g_psxNextBranchCycle = psxRegs.sCycle[n] + psxRegs.eCycle[n]; + psxSetNextBranchDelta( ecycle ); if( psxCycleEE < 0 ) { // The EE called this int, so inform it to branch as needed: - u32 iopDelta = (g_psxNextBranchCycle-psxRegs.cycle)*8; - if( g_nextBranchCycle - cpuRegs.cycle > iopDelta ) - { - // Optimization note: This method inlines witn 'n' as a constant, so the - // following conditionals will optimize nicely. - - g_nextBranchCycle = cpuRegs.cycle + iopDelta; - if( n > 12 ) g_eeTightenSync += 5; - if( n >= 19 ) g_eeTightenSync += 2; - } + s32 iopDelta = (g_psxNextBranchCycle-psxRegs.cycle)*8; + cpuSetNextBranchDelta( iopDelta ); } } +static __forceinline void PSX_TESTINT( u32 n, void (*callback)(), int runIOPcode ) +{ + if( !(psxRegs.interrupt & (1 << n)) ) return; -#define PSX_TESTINT(n, callback) \ - if (psxRegs.interrupt & (1 << n)) { \ - if ((int)(psxRegs.cycle - psxRegs.sCycle[n]) >= psxRegs.eCycle[n]) { \ - callback(); \ - } \ - else if( (int)(g_psxNextBranchCycle - psxRegs.sCycle[n]) > psxRegs.eCycle[n] ) \ - g_psxNextBranchCycle = psxRegs.sCycle[n] + psxRegs.eCycle[n]; \ + if( psxTestCycle( psxRegs.sCycle[n], psxRegs.eCycle[n] ) ) + { + callback(); + //if( runIOPcode ) iopBranchAction = 1; } + else + psxSetNextBranch( psxRegs.sCycle[n], psxRegs.eCycle[n] ); +} static __forceinline void _psxTestInterrupts() { - - PSX_TESTINT(9, sif0Interrupt); // SIF0 - PSX_TESTINT(10, sif1Interrupt); // SIF1 - PSX_TESTINT(16, sioInterrupt); - PSX_TESTINT(19, cdvdReadInterrupt); + PSX_TESTINT(9, sif0Interrupt, 1); // SIF0 + PSX_TESTINT(10, sif1Interrupt, 1); // SIF1 + PSX_TESTINT(16, sioInterrupt, 0); + PSX_TESTINT(19, cdvdReadInterrupt, 1); // Profile-guided Optimization (sorta) // The following ints are rarely called. Encasing them in a conditional @@ -197,24 +222,27 @@ static __forceinline void _psxTestInterrupts() if( psxRegs.interrupt & ( (3ul<<11) | (3ul<<20) | (3ul<<17) ) ) { - PSX_TESTINT(17, cdrInterrupt); - PSX_TESTINT(18, cdrReadInterrupt); - PSX_TESTINT(11, psxDMA11Interrupt); // SIO2 - PSX_TESTINT(12, psxDMA12Interrupt); // SIO2 - PSX_TESTINT(20, dev9Interrupt); - PSX_TESTINT(21, usbInterrupt); + PSX_TESTINT(11, psxDMA11Interrupt,0); // SIO2 + PSX_TESTINT(12, psxDMA12Interrupt,0); // SIO2 + PSX_TESTINT(17, cdrInterrupt,0); + PSX_TESTINT(18, cdrReadInterrupt,0); + PSX_TESTINT(20, dev9Interrupt,1); + PSX_TESTINT(21, usbInterrupt,1); } } void psxBranchTest() { - if ((psxRegs.cycle - psxNextsCounter) >= psxNextCounter) + if( psxTestCycle( psxNextsCounter, psxNextCounter ) ) + { psxRcntUpdate(); + iopBranchAction = 1; + } // start the next branch at the next counter event by default - // the int code below will assign nearer branches if needed. + // the interrupt code below will assign nearer branches if needed. g_psxNextBranchCycle = psxNextsCounter+psxNextCounter; - + if (psxRegs.interrupt) _psxTestInterrupts(); if (psxHu32(0x1078)) { @@ -223,6 +251,7 @@ void psxBranchTest() { // PSXCPU_LOG("Interrupt: %x %x\n", HWMu32(0x1070), HWMu32(0x1074)); psxException(0, 0); + iopBranchAction = 1; } } } diff --git a/pcsx2/R3000A.h b/pcsx2/R3000A.h index e9b3065035..3e7c9b4377 100644 --- a/pcsx2/R3000A.h +++ b/pcsx2/R3000A.h @@ -127,8 +127,8 @@ typedef struct psxRegisters_t { u32 code; /* The instruction */ u32 cycle; u32 interrupt; - u32 sCycle[64]; - u32 eCycle[64]; + u32 sCycle[64]; // start cycle for signaled ints + s32 eCycle[64]; // cycle delta for signaled ints (sCycle + eCycle == branch cycle) u32 _msflag[32]; u32 _smflag[32]; } psxRegisters; @@ -213,4 +213,8 @@ void psxBranchTest(); void psxExecuteBios(); void psxRestartCPU(); +extern s32 psxNextCounter; +extern u32 psxNextsCounter; +extern int iopBranchAction; + #endif /* __R3000A_H__ */ diff --git a/pcsx2/R5900.c b/pcsx2/R5900.c index 5c98b49fb0..523969fb18 100644 --- a/pcsx2/R5900.c +++ b/pcsx2/R5900.c @@ -131,7 +131,7 @@ void cpuReset() fpuRegs.fprc[0] = 0x00002e00; // fpu Revision.. fpuRegs.fprc[31] = 0x01000001; // fpu Status/Control - vu0Reset(); + vu0Reset(); vu1Reset(); hwReset(); vif0Reset(); @@ -325,16 +325,44 @@ void cpuTestMissingHwInts() { } } +// sets a branch test to occur some time from an arbitrary starting point. +__forceinline int cpuSetNextBranch( u32 startCycle, s32 delta ) +{ + // typecast the conditional to signed so that things don't blow up + // if startCycle is greater than our next branch cycle. + + if( (int)(g_nextBranchCycle - startCycle) > delta ) + { + g_nextBranchCycle = startCycle + delta; + return 1; + } + return 0; +} + +// sets a branch to occur some time from the current cycle +__forceinline int cpuSetNextBranchDelta( s32 delta ) +{ + return cpuSetNextBranch( cpuRegs.cycle, delta ); +} + +// tests the cpu cycle agaisnt the given start and delta values. +// Returns true if the delta time has passed. +__forceinline int cpuTestCycle( u32 startCycle, s32 delta ) +{ + // typecast the conditional to signed so that things don't explode + // if the startCycle is ahead of our current cpu cycle. + + return (int)(cpuRegs.cycle - startCycle) >= delta; +} + static __forceinline void TESTINT( u8 n, void (*callback)() ) { if( !(cpuRegs.interrupt & (1 << n)) ) return; - if( (cpuRegs.cycle - cpuRegs.sCycle[n]) >= cpuRegs.eCycle[n] ) - { + if( cpuTestCycle( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ) ) callback(); - } - else if( (g_nextBranchCycle - cpuRegs.sCycle[n]) > cpuRegs.eCycle[n] ) - g_nextBranchCycle = cpuRegs.sCycle[n] + cpuRegs.eCycle[n]; + else + cpuSetNextBranch( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ); } static __forceinline void _cpuTestInterrupts() @@ -373,14 +401,13 @@ u32 s_iLastPERFCycle[2] = {0,0}; static __forceinline void _cpuTestTIMR() { - cpuRegs.CP0.n.Count += cpuRegs.cycle-s_iLastCOP0Cycle; - s_iLastCOP0Cycle = cpuRegs.cycle; + // The interpreter and recompiler both re-calculate these values whenever they + // are read, so updating them at regular intervals is merely a common courtesy. + // For that reason they're part of the Counters event, since it's garaunteed + // to be called at least 100 times a second. - // [Air] : Are these necessary? The recompiler and interpreter code both appear - // to recalculate them whenever they're read. (although if they were not read - // for a long time they could overflow). Maybe these checks could be moved to - // the Ints or Counters so they they get called less frequently, but still get - // called enough to avoid overflows. + // Updating them more frequently is pointless and, in fact, they could + // just as well be updated 20 times a second if it were convenient to do so. if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) { cpuRegs.PERF.n.pcr0 += cpuRegs.cycle-s_iLastPERFCycle[0]; @@ -391,6 +418,9 @@ static __forceinline void _cpuTestTIMR() s_iLastPERFCycle[1] = cpuRegs.cycle; } + cpuRegs.CP0.n.Count += cpuRegs.cycle-s_iLastCOP0Cycle; + s_iLastCOP0Cycle = cpuRegs.cycle; + if ( (cpuRegs.CP0.n.Status.val & 0x8000) && cpuRegs.CP0.n.Count >= cpuRegs.CP0.n.Compare && cpuRegs.CP0.n.Count < cpuRegs.CP0.n.Compare+1000 ) { SysPrintf("timr intr: %x, %x\n", cpuRegs.CP0.n.Count, cpuRegs.CP0.n.Compare); @@ -398,17 +428,10 @@ static __forceinline void _cpuTestTIMR() } } -// maximum wait between branches. Lower values provide a tighter synchronization between +// Maximum wait between branches. Lower values provide a tighter synchronization between // the EE and the IOP, but incur more execution overhead. #define EE_WAIT_CYCLE 2048 -// maximum wait between branches when EE/IOP sync is tightened via g_eeTightenSync. -// Lower values don't always make games better, since places where this value is set -// will have to use higher numbers to achieve the same cycle count. -#define EE_ACTIVE_CYCLE 192 -#define EE_ACTIVE_CYCLE_SUB (EE_WAIT_CYCLE - EE_ACTIVE_CYCLE) - - // if cpuRegs.cycle is greater than this cycle, should check cpuBranchTest for updates u32 g_nextBranchCycle = 0; @@ -416,13 +439,79 @@ u32 g_nextBranchCycle = 0; // synchronization). Value decremented each branch test. u32 g_eeTightenSync = 0; -#if !defined( PCSX2_NORECBUILD ) && !defined( PCSX2_PUBLIC ) +// Shared portion of the branch test, called from both the Interpreter +// and the recompiler. (moved here to help alleviate redundant code) +static __forceinline void _cpuBranchTest_Shared() +{ + g_nextBranchCycle = cpuRegs.cycle + EE_WAIT_CYCLE; + + EEsCycle += cpuRegs.cycle - EEoCycle; + EEoCycle = cpuRegs.cycle; + + iopBranchAction = ( EEsCycle > 0 ); + + // ---- Counters ------------- + + rcntUpdate_hScanline(); + + if( cpuTestCycle( nextsCounter, nextCounter ) ) + { + rcntUpdate(); + _cpuTestTIMR(); + } + + //#ifdef CPU_LOG + // cpuTestMissingHwInts(); + //#endif + + // ---- Interrupts ------------- + + if( cpuRegs.interrupt ) + _cpuTestInterrupts(); + + // ---- IOP ------------- + // * It's important to run a psxBranchTest before calling ExecuteBlock. This + // is because the IOP does not always perform branch tests before returning + // (during the prev branch) and also so it can act on the state the EE has + // given it before executing any code. + // + // * The IOP cannot always be run. If we run IOP code every time through the + // cpuBranchTest, the IOP generally starts to run way ahead of the EE. + // + // * However! The IOP should be run during certain important events: vsync/hsync + // events and IOP interrupts / exceptions -- even if it's already getting + // a little ahead of the EE. the iopBranchAction global will flag true if + // something like that happens. + + psxBranchTest(); + + if( iopBranchAction ) + { + //if( EEsCycle < -400 ) + // SysPrintf( " IOP ahead by: %d\n", -EEsCycle ); + + psxCpu->ExecuteBlock(); + } + + // The IOP cound be running ahead of us, so adjust the iop's next branch by its + // relative position to the EE (via EEsCycle) + cpuSetNextBranchDelta( ((g_psxNextBranchCycle-psxRegs.cycle)*8) - EEsCycle ); + + // Apply the hsync counter's nextCycle + cpuSetNextBranch( counters[4].sCycle, counters[4].CycleT ); + + // Apply other counter nextCycles + cpuSetNextBranch( nextsCounter, nextCounter ); +} + +#ifndef PCSX2_NORECBUILD +#ifndef PCSX2_PUBLIC extern u8 g_globalXMMSaved; X86_32CODE(extern u8 g_globalMMXSaved;) #endif +#endif u32 g_MTGSVifStart = 0, g_MTGSVifCount=0; -extern void gsWaitGS(); void cpuBranchTest() { @@ -436,67 +525,19 @@ void cpuBranchTest() g_EEFreezeRegs = 0; #endif - g_nextBranchCycle = cpuRegs.cycle + EE_WAIT_CYCLE; - if( g_eeTightenSync != 0 ) - { - // This means we're running "sync-sensitive" code. - // tighten up the EE's cycle rate to ensure a more responsive - // EE/IOP operation: - - g_eeTightenSync--; - g_nextBranchCycle -= EE_ACTIVE_CYCLE_SUB; - } - - // ---- Counters ------------- - - if( (cpuRegs.cycle - nextsCounter) >= nextCounter ) - rcntUpdate(); - - if( (g_nextBranchCycle-nextsCounter) >= nextCounter ) - g_nextBranchCycle = nextsCounter+nextCounter; - - // ---- Interrupts ------------- - - if( cpuRegs.interrupt ) - _cpuTestInterrupts(); + // Perform counters, ints, and IOP updates: + _cpuBranchTest_Shared(); // ---- MTGS ------------- // stall mtgs if it is taking too long if( g_MTGSVifCount > 0 ) { - if( cpuRegs.cycle-g_MTGSVifStart > g_MTGSVifCount ) { + if( (int)(cpuRegs.cycle-g_MTGSVifStart) > g_MTGSVifCount ) { gsWaitGS(); g_MTGSVifCount = 0; } } -//#ifdef CPU_LOG -// cpuTestMissingHwInts(); -//#endif - _cpuTestTIMR(); - - // ---- IOP ------------- - // Signal for an immediate branch test! This is important! The IOP must - // be able to act on the state the EE has given it before executing any - // additional code. - - psxBranchTest(); - - EEsCycle += cpuRegs.cycle - EEoCycle; - EEoCycle = cpuRegs.cycle; - - psxCpu->ExecuteBlock(); - - // IOP Synchronization: - // If the IOP needs to branch soon then so should the EE. - // As the master of all, the EE should look out for its children and - // assure them the love they deserve: - { - u32 iopDelta = (g_psxNextBranchCycle-psxRegs.cycle)*8; - if( g_nextBranchCycle - cpuRegs.cycle > iopDelta ) - g_nextBranchCycle = cpuRegs.cycle + iopDelta; - } - // ---- VU0 ------------- if (VU0.VI[REG_VPU_STAT].UL & 0x1) @@ -514,82 +555,64 @@ void cpuBranchTest() #endif } -__forceinline void CPU_INT( u32 n, u32 ecycle) +__forceinline void CPU_INT( u32 n, s32 ecycle) { cpuRegs.interrupt|= 1 << n; cpuRegs.sCycle[n] = cpuRegs.cycle; cpuRegs.eCycle[n] = ecycle; - if( (g_nextBranchCycle - cpuRegs.sCycle[n]) <= cpuRegs.eCycle[n] ) return; + // Interrupt is happening soon: make sure both EE and IOP are aware. - // Interrupt is happening soon: make sure everyone's aware! - g_nextBranchCycle = cpuRegs.sCycle[n] + cpuRegs.eCycle[n]; - - // Optimization note: this method inlines nicely since 'n' is almost always a - // constant. The following conditional optimizes to virtually nothing in most - // cases. - - if( ( n == 3 || n == 4 || n == 30 || n == 31 ) && - ecycle <= 28 && psxCycleEE > 0 ) + if( ecycle <= 28 && psxCycleEE > 0 ) { // If running in the IOP, force it to break immediately into the EE. // the EE's branch test is due to run. - psxBreak += psxCycleEE; // number of cycles we didn't run. + psxBreak += psxCycleEE; // record the number of cycles the IOP didn't run. psxCycleEE = 0; - if( n == 3 || n == 4 ) - g_eeTightenSync += 1; // only tighten IPU a bit, otherwise it's too slow! - else - g_eeTightenSync += 4; } + + cpuSetNextBranchDelta( cpuRegs.eCycle[n] ); } -static void _cpuTestINTC() { - if ((cpuRegs.CP0.n.Status.val & 0x10407) == 0x10401){ - if (psHu32(INTC_STAT) & psHu32(INTC_MASK)) { - if ((cpuRegs.interrupt & (1 << 30)) == 0) { - CPU_INT(30,4); - } - } - } +__forceinline void cpuTestINTCInts() { + if( (cpuRegs.CP0.n.Status.val & 0x10407) != 0x10401 ) return; + if( (psHu32(INTC_STAT) & psHu32(INTC_MASK)) == 0 ) return; + if( cpuRegs.interrupt & (1 << 30) ) return; + + // fixme: The counters code throws INT30's alot, and most of the time they're + // "late" already, so firing them immediately instead of after the next branch + // (in which case they'll be really late) would be a lot better in theory. + // However, setting this to zero for everything breaks games, so if it's done + // it needs to be done for counters only. + + CPU_INT(30,4); } -static void _cpuTestDMAC() { - if ((cpuRegs.CP0.n.Status.val & 0x10807) == 0x10801){ - if (psHu16(0xe012) & psHu16(0xe010) || - psHu16(0xe010) & 0x8000) { - if ( (cpuRegs.interrupt & (1 << 31)) == 0) { - CPU_INT(31, 4); - } - } - } +__forceinline void cpuTestDMACInts() { + if ((cpuRegs.CP0.n.Status.val & 0x10807) != 0x10801) return; + if ( cpuRegs.interrupt & (1 << 31) ) return; + + if ( ( (psHu16(0xe012) & psHu16(0xe010)) == 0) && + ( (psHu16(0xe010) & 0x8000) == 0) ) return; + + CPU_INT(31, 4); } -void cpuTestHwInts() { - //if ((cpuRegs.CP0.n.Status.val & 0x10007) != 0x10001) return; - _cpuTestINTC(); - _cpuTestDMAC(); - _cpuTestTIMR(); -} - -void cpuTestINTCInts() { - //if ((cpuRegs.CP0.n.Status.val & 0x10407) == 0x10401) { - _cpuTestINTC(); - //} -} - -void cpuTestDMACInts() { - //if ((cpuRegs.CP0.n.Status.val & 0x10807) == 0x10801) { - _cpuTestDMAC(); - //} -} - -void cpuTestTIMRInts() { +// fixme: Unused code. delete or find its true purpose? +__forceinline void cpuTestTIMRInts() { if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) { _cpuTestTIMR(); } } +// fixme: unused code. delete or find its true purpose? +void cpuTestHwInts() { + cpuTestINTCInts(); + cpuTestDMACInts(); + cpuTestTIMRInts(); +} + void cpuExecuteBios() { // filter CPU options @@ -601,41 +624,20 @@ void cpuExecuteBios() case PCSX2_FRAMELIMIT_SKIP: case PCSX2_FRAMELIMIT_VUSKIP: if( GSsetFrameSkip == NULL ) + { Config.Options &= ~PCSX2_FRAMELIMIT_MASK; + SysPrintf("Notice: Disabling frameskip -- GS plugin does not support it.\n"); + } break; } - SysPrintf("Using Frame Skipping: "); - switch(CHECK_FRAMELIMIT) { - case PCSX2_FRAMELIMIT_NORMAL: SysPrintf("Normal\n"); break; - case PCSX2_FRAMELIMIT_LIMIT: SysPrintf("Limit\n"); break; - case PCSX2_FRAMELIMIT_SKIP: SysPrintf("Skip\n"); break; - case PCSX2_FRAMELIMIT_VUSKIP: SysPrintf("VU Skip\n"); break; - } - - //? if(CHECK_FRAMELIMIT==PCSX2_FRAMELIMIT_LIMIT) - { - extern u64 GetTickFrequency(); - extern u64 iTicks; - if (Config.CustomFps > 0) { - iTicks = GetTickFrequency() / Config.CustomFps; - SysPrintf("Framelimiter rate updated (UpdateVSyncRate): %d fps\n", Config.CustomFps); - } - else if (Config.PsxType & 1) { - iTicks = (GetTickFrequency() / 5000) * 100; - SysPrintf("Framelimiter rate updated (UpdateVSyncRate): 50 fps\n"); - } - else { - iTicks = (GetTickFrequency() / 5994) * 100; - SysPrintf("Framelimiter rate updated (UpdateVSyncRate): 59.94 fps\n"); - } - } - + UpdateVSyncRate(); SysPrintf("* PCSX2 *: ExecuteBios\n"); bExecBIOS = TRUE; while (cpuRegs.pc != 0x00200008 && cpuRegs.pc != 0x00100008) { + g_nextBranchCycle = cpuRegs.cycle; Cpu->ExecuteBlock(); } @@ -687,30 +689,9 @@ void IntcpuBranchTest() g_EEFreezeRegs = 0; #endif - // Interpreter uses a high-resolution wait cycle all the time: - g_nextBranchCycle = cpuRegs.cycle + 256; + // Perform counters, ints, and IOP updates: + _cpuBranchTest_Shared(); - if ((cpuRegs.cycle - nextsCounter) >= nextCounter) - rcntUpdate(); - - if (cpuRegs.interrupt) - _cpuTestInterrupts(); - - if( (g_nextBranchCycle-nextsCounter) >= nextCounter ) - g_nextBranchCycle = nextsCounter+nextCounter; - -//#ifdef CPU_LOG -// cpuTestMissingHwInts(); -//#endif - _cpuTestTIMR(); - - psxBranchTest(); - - EEsCycle += cpuRegs.cycle - EEoCycle; - EEoCycle = cpuRegs.cycle; - - psxCpu->ExecuteBlock(); - if (VU0.VI[REG_VPU_STAT].UL & 0x1) { Cpu->ExecuteVU0Block(); } diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h index 5911189bdc..b6665a53c4 100644 --- a/pcsx2/R5900.h +++ b/pcsx2/R5900.h @@ -233,9 +233,9 @@ void cpuTlbMissW(u32 addr, u32 bd); void IntcpuBranchTest(); void cpuBranchTest(); void cpuTestHwInts(); -void cpuTestINTCInts(); -void cpuTestDMACInts(); -void cpuTestTIMRInts(); +extern void cpuTestINTCInts(); +extern void cpuTestDMACInts(); +extern void cpuTestTIMRInts(); void cpuExecuteBios(); void cpuRestartCPU(); @@ -251,7 +251,6 @@ void JumpCheckSym(u32 addr, u32 pc); void JumpCheckSymRet(u32 addr); extern u32 g_EEFreezeRegs; -extern u32 g_eeTightenSync; // non-zero values tighten EE/IOP code synchronization for short periods. //exception code #define EXC_CODE(x) ((x)<<2) diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index 5cfa3158de..44c4506ee9 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -59,7 +59,6 @@ extern "C" { #include "iR3000A.h" #include "PsxCounters.h" -extern u32 psxNextCounter, psxNextsCounter; u32 g_psxMaxRecMem = 0; extern char *disRNameGPR[]; extern char* disR3000Fasm(u32 code, u32 pc); @@ -445,7 +444,6 @@ void psxRecompileCodeConst0(R3000AFNPTR constcode, R3000AFNPTR_INFO constscode, } extern "C" void zeroEx(); -extern "C" u32 g_eeTightenSync; // rt = rs op imm16 void psxRecompileCodeConst1(R3000AFNPTR constcode, R3000AFNPTR_INFO noconstcode) @@ -459,11 +457,10 @@ void psxRecompileCodeConst1(R3000AFNPTR constcode, R3000AFNPTR_INFO noconstcode) _psxFlushCall(FLUSH_NODESTROY); CALLFunc((uptr)zeroEx); #endif - // Tighten up the EE/IOP sync (helps prevent crashes) - // [TODO] should probably invoke a branch test or EE code control break here, - // but it would require the use of registers and I have no eff'ing idea how - // the register allocation stuff works in the recompiler. :/ - ADD32ItoM( (uptr)&g_eeTightenSync, 3 ); + // Bios Call: Force the IOP to do a Branch Test ASAP. + // Important! This helps prevent game freeze-ups during boot-up and stage loads. + MOV32MtoR( EAX, (uptr)&psxRegs.cycle ); + MOV32RtoM( (uptr)&g_psxNextBranchCycle, EAX ); } return; } @@ -1021,7 +1018,7 @@ static void iPsxBranchTest(u32 newpc, u32 cpuBranch) MOV32RtoM((uptr)&psxRegs.cycle, ECX); // update cycles MOV32RtoM((uptr)&psxCycleEE, EAX); - j8Ptr[2] = JNS8( 0 ); // jump if no, on (psxCycleEE - blockCycles*8) < 0 + j8Ptr[2] = JG8( 0 ); // jump if psxCycleEE > blockCycles*8 if( REC_INC_STACK ) ADD64ItoR(ESP, REC_INC_STACK); @@ -1072,9 +1069,12 @@ void rpsxSYSCALL() CMP32ItoM((uptr)&psxRegs.pc, psxpc-4); j8Ptr[0] = JE8(0); + ADD32ItoM((uptr)&psxRegs.cycle, psxScaleBlockCycles() ); SUB32ItoM((uptr)&psxCycleEE, psxScaleBlockCycles()*8 ); JMP32((uptr)psxDispatcherReg - ( (uptr)x86Ptr + 5 )); + + // jump target for skipping blockCycle updates x86SetJ8(j8Ptr[0]); //if (!psxbranch) psxbranch = 2; @@ -1117,9 +1117,6 @@ void psxRecompileNextInstruction(int delayslot) BASEBLOCK* pblock = PSX_GETBLOCK(psxpc); - //if( psxpc == 0x5264 ) - // SysPrintf( "Woot!" ); - // need *ppblock != s_pCurBlock because of branches if( pblock->pFnptr != 0 && pblock->startpc != s_pCurBlock->startpc ) { diff --git a/pcsx2/x86/ix86-64/iR5900-64.c b/pcsx2/x86/ix86-64/iR5900-64.c index 43576df142..594822bb3c 100644 --- a/pcsx2/x86/ix86-64/iR5900-64.c +++ b/pcsx2/x86/ix86-64/iR5900-64.c @@ -1776,16 +1776,15 @@ static void iBranchTest(u32 newpc, u32 cpuBranch) //CALLFunc((uptr)testfpu); #endif - if( !USE_FAST_BRANCHES || cpuBranch ) { - MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); - ADD32ItoR(ECX, s_nBlockCycles * EECYCLE_MULT); // NOTE: mulitply cycles here, 6/5 ratio stops pal ffx from randomly crashing, but crashes jakI - MOV32RtoM((uptr)&cpuRegs.cycle, ECX); // update cycles - } - else { + if( USE_FAST_BRANCHES && (cpuBranch==0) ) + { ADD32ItoM((uptr)&cpuRegs.cycle, s_nBlockCycles*9/8); return; } + MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); + ADD32ItoR(ECX, s_nBlockCycles * EECYCLE_MULT); + MOV32RtoM((uptr)&cpuRegs.cycle, ECX); // update cycles SUB32MtoR(ECX, (uptr)&g_nextBranchCycle); // check if should branch