diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 0e80ddc3a7..9d9123139f 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -92,43 +92,40 @@ static std::mutex externalEventLock; std::vector mhzChangeCallbacks; void FireMhzChange() { - for (auto it = mhzChangeCallbacks.begin(), end = mhzChangeCallbacks.end(); it != end; ++it) { - MHzChangeCallback cb = *it; + for (MHzChangeCallback cb : mhzChangeCallbacks) { cb(); } } -void SetClockFrequencyMHz(int cpuMhz) -{ +void SetClockFrequencyHz(int cpuHz) { // When the mhz changes, we keep track of what "time" it was before hand. // This way, time always moves forward, even if mhz is changed. lastGlobalTimeUs = GetGlobalTimeUs(); lastGlobalTimeTicks = GetTicks(); - CPU_HZ = cpuMhz * 1000000; + CPU_HZ = cpuHz; // TODO: Rescale times of scheduled events? FireMhzChange(); } -int GetClockFrequencyMHz() -{ - return CPU_HZ / 1000000; +int GetClockFrequencyHz() { + return CPU_HZ; } -u64 GetGlobalTimeUsScaled() -{ - s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks; - int freq = GetClockFrequencyMHz(); - s64 usSinceLast = ticksSinceLast / freq; - return lastGlobalTimeUs + usSinceLast; +u64 GetGlobalTimeUsScaled() { + return GetGlobalTimeUs(); } -u64 GetGlobalTimeUs() -{ +u64 GetGlobalTimeUs() { s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks; - int freq = GetClockFrequencyMHz(); - s64 usSinceLast = ticksSinceLast / freq; + int freq = GetClockFrequencyHz(); + s64 usSinceLast = ticksSinceLast * 1000000 / freq; + if (ticksSinceLast > UINT_MAX) { + // Adjust the calculated value to avoid overflow errors. + lastGlobalTimeUs += usSinceLast; + lastGlobalTimeTicks = GetTicks(); + } return lastGlobalTimeUs + usSinceLast; } diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index ee7a26a714..4c5cb013c8 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -67,7 +67,7 @@ inline s64 usToCycles(u64 us) { } inline s64 cyclesToUs(s64 cycles) { - return cycles / (CPU_HZ / 1000000); + return (cycles * 1000000) / CPU_HZ; } namespace CoreTiming @@ -121,8 +121,8 @@ namespace CoreTiming void DoState(PointerWrap &p); - void SetClockFrequencyMHz(int cpuMhz); - int GetClockFrequencyMHz(); + void SetClockFrequencyHz(int cpuHz); + int GetClockFrequencyHz(); extern int slicelength; }; // end of namespace diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 6a36edb0d5..de093d830c 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -1069,10 +1069,10 @@ static u32 sceDisplayGetVcount() { } static u32 __DisplayGetCurrentHcount() { - const static int ticksPerVblank333 = 333 * 1000000 / 60 / hCountPerVblank; const int ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks; + const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank; // Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right. - return 1 + (ticksIntoFrame / (CoreTiming::GetClockFrequencyMHz() * ticksPerVblank333 / 333)); + return 1 + (ticksIntoFrame / ticksPerVblank); } static u32 __DisplayGetAccumulatedHcount() { diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index 8204a51c58..b4fe3b0925 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -57,10 +57,57 @@ static int powerCbSlots[numberOfCBPowerSlots]; static std::vector volatileWaitingThreads; // Should this belong here, or in CoreTiming? -static int RealpllFreq = 222; -static int RealbusFreq = 111; -static int pllFreq = 222; -static int busFreq = 111; +static int RealpllFreq = 222000000; +static int RealbusFreq = 111000000; +static int pllFreq = 222000000; +static int busFreq = 111000000; + +// The CPU mhz can only be a multiple of the PLL divided by 511. +int PowerCpuMhzToHz(int desired, int pllHz) { + double maxfreq = desired * 1000000.0; + + double step = (double)pllHz / 511.0; + // These values seem to be locked. + if (pllHz >= 333000000 && desired == 333) { + return 333000000; + } else if (pllHz >= 222000000 && desired == 222) { + return 222000000; + } + + double freq = 0; + while (freq + step < maxfreq) { + freq += step; + } + + // We match the PSP's HLE funcs better when we have the same float error, it seems. + return (float)(freq / 1000000.0f) * 1000000; +} + +int PowerPllMhzToHz(int mhz) { + // These seem to be the only steps it has. + if (mhz <= 190) + return 190285721; + if (mhz <= 222) + return 222000000; + if (mhz <= 266) + return 266399994; + if (mhz <= 333) + return 333000000; + return mhz * 1000000; +} + +int PowerBusMhzToHz(int mhz) { + // These seem to be the only steps it has. + if (mhz <= 95) + return 95142860; + if (mhz <= 111) + return 111000000; + if (mhz <= 133) + return 133199997; + if (mhz <= 166) + return 166500000; + return mhz * 1000000; +} void __PowerInit() { memset(powerCbSlots, 0, sizeof(powerCbSlots)); @@ -68,15 +115,15 @@ void __PowerInit() { volatileWaitingThreads.clear(); if (g_Config.iLockedCPUSpeed > 0) { - CoreTiming::SetClockFrequencyMHz(g_Config.iLockedCPUSpeed); - pllFreq = g_Config.iLockedCPUSpeed; - busFreq = g_Config.iLockedCPUSpeed / 2; + pllFreq = PowerPllMhzToHz(g_Config.iLockedCPUSpeed); + busFreq = PowerBusMhzToHz(pllFreq / 2000000); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(g_Config.iLockedCPUSpeed, pllFreq)); } else { - pllFreq = 222; - busFreq = 111; + pllFreq = PowerPllMhzToHz(222); + busFreq = PowerBusMhzToHz(111); } - RealpllFreq = 222; - RealbusFreq = 111; + RealpllFreq = PowerPllMhzToHz(222); + RealbusFreq = PowerBusMhzToHz(111); } void __PowerDoState(PointerWrap &p) { @@ -87,17 +134,20 @@ void __PowerDoState(PointerWrap &p) { if (s >= 2) { p.Do(RealpllFreq); p.Do(RealbusFreq); - } - else { - RealpllFreq = 222; - RealbusFreq = 111; + + if (RealpllFreq < 1000000) + RealpllFreq = PowerPllMhzToHz(RealpllFreq); + if (RealbusFreq < 1000000) + RealbusFreq = PowerBusMhzToHz(RealbusFreq); + } else { + RealpllFreq = PowerPllMhzToHz(222); + RealbusFreq = PowerBusMhzToHz(111); } if (g_Config.iLockedCPUSpeed > 0) { - CoreTiming::SetClockFrequencyMHz(g_Config.iLockedCPUSpeed); - pllFreq = g_Config.iLockedCPUSpeed; - busFreq = g_Config.iLockedCPUSpeed / 2; - } - else { + pllFreq = PowerPllMhzToHz(g_Config.iLockedCPUSpeed); + busFreq = PowerBusMhzToHz(pllFreq / 2000000); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(g_Config.iLockedCPUSpeed, pllFreq)); + } else { pllFreq = RealpllFreq; busFreq = RealbusFreq; } @@ -372,104 +422,124 @@ static int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) { static u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) { + // 190 might (probably) be a typo for 19, but it's what the actual PSP validates against. + if (pllfreq < 19 || pllfreq < cpufreq || pllfreq > 333) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid pll frequency"); + } if (cpufreq == 0 || cpufreq > 333) { - WARN_LOG(HLE,"scePowerSetClockFrequency(%i,%i,%i): invalid frequency", pllfreq, cpufreq, busfreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid cpu frequency"); + } + if (busfreq == 0 || busfreq > 166) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid bus frequency"); } // TODO: More restrictions. if (g_Config.iLockedCPUSpeed > 0) { INFO_LOG(HLE, "scePowerSetClockFrequency(%i,%i,%i): locked by user config at %i, %i, %i", pllfreq, cpufreq, busfreq, g_Config.iLockedCPUSpeed, g_Config.iLockedCPUSpeed, busFreq); - } - else { + } else { INFO_LOG(HLE, "scePowerSetClockFrequency(%i,%i,%i)", pllfreq, cpufreq, busfreq); } - if (RealpllFreq != pllfreq || RealbusFreq != busfreq) { - RealpllFreq = pllfreq; - RealbusFreq = busfreq; + // Only reschedules when the stepped PLL frequency changes. + // It seems like the busfreq parameter has no effect (but can cause errors.) + if (RealpllFreq != PowerPllMhzToHz(pllfreq)) { + int oldPll = RealpllFreq / 1000000; + + RealpllFreq = PowerPllMhzToHz(pllfreq); + RealbusFreq = PowerBusMhzToHz(RealpllFreq / 2000000); if (g_Config.iLockedCPUSpeed <= 0) { - pllFreq = pllfreq; - busFreq = busfreq; - CoreTiming::SetClockFrequencyMHz(cpufreq); + pllFreq = RealpllFreq; + busFreq = RealbusFreq; + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); } - return hleDelayResult(0, "scepower set clockFrequency", 150000); + + // The delay depends on the source and destination frequency, most are 150ms. + int newPll = RealpllFreq / 1000000; + int usec = 150000; + if ((newPll == 190 && oldPll == 222) || (newPll == 222 && oldPll == 190)) + usec = 15700; + else if ((newPll == 266 && oldPll == 333) || (newPll == 333 && oldPll == 266)) + usec = 16600; + + return hleDelayResult(0, "scepower set clockFrequency", usec); } if (g_Config.iLockedCPUSpeed <= 0) - CoreTiming::SetClockFrequencyMHz(cpufreq); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); return 0; } static u32 scePowerSetCpuClockFrequency(u32 cpufreq) { - if(g_Config.iLockedCPUSpeed > 0) { - DEBUG_LOG(HLE,"scePowerSetCpuClockFrequency(%i): locked by user config at %i", cpufreq, g_Config.iLockedCPUSpeed); + if (cpufreq == 0 || cpufreq > 333) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency"); } - else { - if (cpufreq == 0 || cpufreq > 333) { - WARN_LOG(HLE,"scePowerSetCpuClockFrequency(%i): invalid frequency", cpufreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; - } - CoreTiming::SetClockFrequencyMHz(cpufreq); - INFO_LOG(HLE,"scePowerSetCpuClockFrequency(%i)", cpufreq); + if (g_Config.iLockedCPUSpeed > 0) { + return hleLogDebug(SCEMISC, 0, "locked by user config at %i", g_Config.iLockedCPUSpeed); } - return 0; + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); + return hleLogSuccessI(SCEMISC, 0); } static u32 scePowerSetBusClockFrequency(u32 busfreq) { - if(g_Config.iLockedCPUSpeed > 0) { - DEBUG_LOG(HLE,"scePowerSetBusClockFrequency(%i): locked by user config at %i", busfreq, busFreq); + if (busfreq == 0 || busfreq > 111) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency"); } - else { - if (busfreq == 0 || busfreq > 111) { - WARN_LOG(HLE,"scePowerSetBusClockFrequency(%i): invalid frequency", busfreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; - } - // TODO: It seems related to other frequencies, though. - busFreq = busfreq; - INFO_LOG_REPORT_ONCE(SetBusClockFrequency,HLE,"scePowerSetBusClockFrequency(%i)", busfreq); + if (g_Config.iLockedCPUSpeed > 0) { + return hleLogDebug(SCEMISC, 0, "locked by user config at %i", g_Config.iLockedCPUSpeed / 2); } - return 0; + + // The value passed is validated, but then doesn't seem to matter for the result. + // However, this sets a different hz than scePowerSetClockFrequency would have. + + if (pllFreq <= 190) + busFreq = 94956673; + else if (pllFreq <= 222) + busFreq = 111000000; + else if (pllFreq <= 266) + busFreq = 132939331; + else if (pllFreq <= 333) + busFreq = 165848343; + else + busFreq = pllFreq / 2; + + return hleLogSuccessI(SCEMISC, 0); } static u32 scePowerGetCpuClockFrequencyInt() { - int cpuFreq = CoreTiming::GetClockFrequencyMHz(); - DEBUG_LOG(HLE,"%i=scePowerGetCpuClockFrequencyInt()", cpuFreq); - return cpuFreq; + int cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000; + return hleLogSuccessI(SCEMISC, cpuFreq); } static u32 scePowerGetPllClockFrequencyInt() { - INFO_LOG(HLE,"%i=scePowerGetPllClockFrequencyInt()", pllFreq); - return pllFreq; + return hleLogSuccessInfoI(SCEMISC, pllFreq / 1000000); } static u32 scePowerGetBusClockFrequencyInt() { - INFO_LOG(HLE,"%i=scePowerGetBusClockFrequencyInt()", busFreq); - return busFreq; + return hleLogSuccessInfoI(SCEMISC, busFreq / 1000000); } static float scePowerGetCpuClockFrequencyFloat() { - int cpuFreq = CoreTiming::GetClockFrequencyMHz(); - DEBUG_LOG(HLE, "%f=scePowerGetCpuClockFrequencyFloat()", (float)cpuFreq); - return (float) cpuFreq; + float cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000.0f; + DEBUG_LOG(SCEMISC, "%f=scePowerGetCpuClockFrequencyFloat()", (float)cpuFreq); + return cpuFreq; } static float scePowerGetPllClockFrequencyFloat() { - INFO_LOG(HLE, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq); - return (float) pllFreq; + INFO_LOG(SCEMISC, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq / 1000000.0f); + return (float) pllFreq / 1000000.0f; } static float scePowerGetBusClockFrequencyFloat() { - INFO_LOG(HLE, "%f=scePowerGetBusClockFrequencyFloat()", (float)busFreq); - return (float) busFreq; + INFO_LOG(SCEMISC, "%f=scePowerGetBusClockFrequencyFloat()", (float)busFreq / 1000000.0f); + return (float) busFreq / 1000000.0f; } static int scePowerTick() { - DEBUG_LOG(HLE, "scePowerTick()"); + DEBUG_LOG(SCEMISC, "scePowerTick()"); // Don't think we need to do anything. return 0; } static u32 IsPSPNonFat() { - DEBUG_LOG(HLE, "%d=scePower_a85880d0_IsPSPNonFat()", g_Config.iPSPModel); + DEBUG_LOG(SCEMISC, "%d=scePower_a85880d0_IsPSPNonFat()", g_Config.iPSPModel); return g_Config.iPSPModel; } diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index 1abf148d7c..755e35f57c 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -247,6 +247,9 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { } LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc)); +#ifdef MASKED_PSP_MEMORY + ANDI2R(SCRATCH1, SCRATCH1, 0x3FFFFFFF); +#endif LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64); LSR(SCRATCH2, SCRATCH1, 24); // or UBFX(SCRATCH2, SCRATCH1, 24, 8) ANDI2R(SCRATCH1, SCRATCH1, 0x00FFFFFF); diff --git a/Core/MIPS/ARM64/Arm64CompALU.cpp b/Core/MIPS/ARM64/Arm64CompALU.cpp index aa3ffb577e..f97ba0dee7 100644 --- a/Core/MIPS/ARM64/Arm64CompALU.cpp +++ b/Core/MIPS/ARM64/Arm64CompALU.cpp @@ -43,7 +43,7 @@ using namespace MIPSAnalyst; // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +//#define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { Comp_Generic(op); return; } diff --git a/Core/MIPS/ARM64/Arm64CompFPU.cpp b/Core/MIPS/ARM64/Arm64CompFPU.cpp index 98121615b1..efe6887f23 100644 --- a/Core/MIPS/ARM64/Arm64CompFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompFPU.cpp @@ -51,7 +51,7 @@ // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { Comp_Generic(op); return; } @@ -102,7 +102,11 @@ void Arm64Jit::Comp_FPULS(MIPSOpcode op) fpr.SpillLock(ft); fpr.MapReg(ft, MAP_NOINIT | MAP_DIRTY); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = offset + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1, addr); } else { gpr.MapReg(rs); @@ -129,7 +133,11 @@ void Arm64Jit::Comp_FPULS(MIPSOpcode op) fpr.MapReg(ft); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = offset + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1, addr); } else { gpr.MapReg(rs); diff --git a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp index e322d4a9a7..a4d647d98b 100644 --- a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp +++ b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp @@ -41,7 +41,7 @@ // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +//#define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { Comp_Generic(op); return; } @@ -56,6 +56,9 @@ namespace MIPSComp { } else { MOV(SCRATCH1, gpr.R(rs)); } +#ifdef MASKED_PSP_MEMORY + ANDI2R(SCRATCH1, SCRATCH1, 0x3FFFFFFF); +#endif } std::vector Arm64Jit::SetScratch1ForSafeAddress(MIPSGPReg rs, s16 offset, ARM64Reg tempReg) { @@ -135,12 +138,17 @@ namespace MIPSComp { std::vector skips; if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = iaddr & 0x3FFFFFFF; +#else + u32 addr = iaddr; +#endif // Need to initialize since this only loads part of the register. // But rs no longer matters (even if rs == rt) since we have the address. gpr.MapReg(rt, load ? MAP_DIRTY : 0); - gpr.SetRegImm(SCRATCH1, iaddr & ~3); + gpr.SetRegImm(SCRATCH1, addr & ~3); - u8 shift = (iaddr & 3) * 8; + u8 shift = (addr & 3) * 8; switch (o) { case 34: // lwl @@ -347,7 +355,12 @@ namespace MIPSComp { } if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) { - if (offset == 0) { +#ifdef MASKED_PSP_MEMORY + u32 addr = iaddr & 0x3FFFFFFF; +#else + u32 addr = iaddr; +#endif + if (addr == iaddr && offset == 0) { // It was already safe. Let's shove it into a reg and use it directly. if (targetReg == INVALID_REG) { load ? gpr.MapDirtyIn(rt, rs) : gpr.MapInIn(rt, rs); @@ -360,7 +373,7 @@ namespace MIPSComp { gpr.MapReg(rt, load ? MAP_NOINIT : 0); targetReg = gpr.R(rt); } - gpr.SetRegImm(SCRATCH1, iaddr); + gpr.SetRegImm(SCRATCH1, addr); addrReg = SCRATCH1; } } else { diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index b51e730208..22b618aaea 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -37,7 +37,7 @@ // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { fpr.ReleaseSpillLocksAndDiscardTemps(); Comp_Generic(op); return; } +// #define CONDITIONAL_DISABLE(flag) { fpr.ReleaseSpillLocksAndDiscardTemps(); Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { fpr.ReleaseSpillLocksAndDiscardTemps(); Comp_Generic(op); return; } @@ -222,7 +222,11 @@ namespace MIPSComp { // CC might be set by slow path below, so load regs first. fpr.MapRegV(vt, MAP_DIRTY | MAP_NOINIT); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = offset + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1, addr); } else { gpr.MapReg(rs); @@ -251,7 +255,11 @@ namespace MIPSComp { // CC might be set by slow path below, so load regs first. fpr.MapRegV(vt); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = offset + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1, addr); } else { gpr.MapReg(rs); @@ -293,7 +301,11 @@ namespace MIPSComp { fpr.MapRegsAndSpillLockV(vregs, V_Quad, MAP_DIRTY | MAP_NOINIT); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (imm + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = imm + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1_64, addr + (uintptr_t)Memory::base); } else { gpr.MapReg(rs); @@ -326,7 +338,11 @@ namespace MIPSComp { fpr.MapRegsAndSpillLockV(vregs, V_Quad, 0); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (imm + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = imm + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1_64, addr + (uintptr_t)Memory::base); } else { gpr.MapReg(rs); @@ -1080,7 +1096,7 @@ namespace MIPSComp { fpr.ReleaseSpillLocksAndDiscardTemps(); } else { fpr.MapRegV(vd); - MOVI2F(fpr.V(vd), 0.0f, SCRATCH1); + fp.MOVI2F(fpr.V(vd), 0.0f, SCRATCH1); } } diff --git a/Core/MIPS/ARM64/Arm64RegCache.cpp b/Core/MIPS/ARM64/Arm64RegCache.cpp index dd2c83c302..b089efa309 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.cpp +++ b/Core/MIPS/ARM64/Arm64RegCache.cpp @@ -440,6 +440,9 @@ Arm64Gen::ARM64Reg Arm64RegCache::MapRegAsPointer(MIPSGPReg reg) { if (!jo_->enablePointerify) { // Convert to a pointer by adding the base and clearing off the top bits. // If SP, we can probably avoid the top bit clear, let's play with that later. +#ifdef MASKED_PSP_MEMORY + emit_->ANDI2R(EncodeRegTo64(a), EncodeRegTo64(a), 0x3FFFFFFF); +#endif emit_->ADD(EncodeRegTo64(a), EncodeRegTo64(a), MEMBASEREG); mr[reg].loc = ML_ARMREG_AS_PTR; } else if (!ar[a].pointerified) { diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index e319cee888..a6ad66a383 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1651,6 +1651,7 @@ namespace MIPSComp { // Vector quaternion product (n = 4, weird prefixes) // d[0 .. 2] = t[0 .. 2] X s[0 .. 2] + s[3] * t[0 .. 2] + t[3] * s[0 .. 2] // d[3] = s[3]*t[3] - s[0 .. 2] dot t[0 .. 3] + // Note: Behaves as if it's implemented through a series of vdots. VectorSize sz = GetVecSize(op); int n = GetNumVectorElements(sz); diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index b390b4cfe3..1314b1239e 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -96,8 +96,7 @@ inline float nanclamp(float f, float lower, float upper) } -void ApplyPrefixST(float *r, u32 data, VectorSize size, float invalid = 0.0f) -{ +void ApplyPrefixST(float *r, u32 data, VectorSize size, float invalid = 0.0f) { // Possible optimization shortcut: if (data == 0xe4) return; @@ -106,26 +105,25 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size, float invalid = 0.0f) float origV[4]{ invalid, invalid, invalid, invalid }; static const float constantArray[8] = {0.f, 1.f, 2.f, 0.5f, 3.f, 1.f/3.f, 0.25f, 1.f/6.f}; - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { origV[i] = r[i]; } - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { int regnum = (data >> (i*2)) & 3; int abs = (data >> (8+i)) & 1; int negate = (data >> (16+i)) & 1; int constants = (data >> (12+i)) & 1; - if (!constants) - { + if (!constants) { + if (regnum >= n) { + // We mostly handle this now, but still worth reporting. + ERROR_LOG_REPORT(CPU, "Invalid VFPU swizzle: %08x: %i / %d at PC = %08x (%s)", data, regnum, n, currentMIPS->pc, MIPSDisasmAt(currentMIPS->pc)); + } r[i] = origV[regnum]; if (abs) ((u32 *)r)[i] = ((u32 *)r)[i] & 0x7FFFFFFF; - } - else - { + } else { r[i] = constantArray[regnum + (abs<<2)]; } @@ -348,11 +346,23 @@ namespace MIPSInt int off = GetMatrixSide(sz) - 1; u32 sprefixRemove = VFPU_ANY_SWIZZLE(); - u32 sprefixAdd = VFPU_CONST(1, 1, 1, 1); + u32 sprefixAdd; switch ((op >> 16) & 0xF) { - case 3: sprefixAdd |= VFPU_SWIZZLE(off == 0, off == 1, off == 2, off == 3); break; - case 6: sprefixAdd |= VFPU_SWIZZLE(0, 0, 0, 0); break; - case 7: sprefixAdd |= VFPU_SWIZZLE(1, 1, 1, 1); break; + case 3: + { + VFPUConst constX = off == 0 ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constY = off == 1 ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constZ = off == 2 ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constW = off == 3 ? VFPUConst::ONE : VFPUConst::ZERO; + sprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW); + break; + } + case 6: + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO); + break; + case 7: + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE); + break; } ApplyPrefixST(&prefixed[off * 4], VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad); WriteMatrix(prefixed, sz, vd); @@ -370,10 +380,10 @@ namespace MIPSInt VectorSize sz = GetVecSize(op); float d[4]; - int constant = 0; + VFPUConst constant = VFPUConst::ZERO; switch ((op >> 16) & 0xF) { - case 6: constant = 0; break; //vzero - case 7: constant = 1; break; //vone + case 6: constant = VFPUConst::ZERO; break; //vzero + case 7: constant = VFPUConst::ONE; break; //vone default: _dbg_assert_msg_(CPU, 0, "Trying to interpret instruction that can't be interpreted"); PC += 4; @@ -383,7 +393,7 @@ namespace MIPSInt // The S prefix generates constants, but negate is still respected. u32 sprefixRemove = VFPU_ANY_SWIZZLE(); - u32 sprefixAdd = VFPU_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(constant, constant, constant, constant); + u32 sprefixAdd = VFPU_MAKE_CONSTANTS(constant, constant, constant, constant); ApplyPrefixST(d, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz); ApplyPrefixD(d, sz); @@ -422,8 +432,14 @@ namespace MIPSInt // The S prefix generates constants, but negate is still respected. int offmask = sz == V_Quad || sz == V_Triple ? 3 : 1; int off = vd & offmask; + // If it's a pair, the identity starts in a different position. + VFPUConst constX = off == (0 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constY = off == (1 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constZ = off == (2 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constW = off == (3 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO; + u32 sprefixRemove = VFPU_ANY_SWIZZLE(); - u32 sprefixAdd = VFPU_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(off == 0, off == 1, off == (2 & offmask), off == (3 & offmask)); + u32 sprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW); ApplyPrefixST(f, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz); ApplyPrefixD(f, sz); @@ -645,7 +661,7 @@ namespace MIPSInt // T prefix forces constants on and regnum to 1. // That means negate still works, and abs activates a different constant. u32 tprefixRemove = VFPU_ANY_SWIZZLE(); - u32 tprefixAdd = VFPU_SWIZZLE(1, 1, 1, 1) | VFPU_CONST(1, 1, 1, 1); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); for (int i = 0; i < GetNumVectorElements(sz); i++) { @@ -675,10 +691,10 @@ namespace MIPSInt u32 sprefixAdd = VFPU_SWIZZLE(0, 0, 1, 1) | VFPU_NEGATE(1, 0, 1, 0); ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), outSize); - // T prefix forces constants on and regnum to 0, 1, 0, 1. + // T prefix forces constants on and regnum to 1, 0, 1, 0. // That means negate still works, and abs activates a different constant. u32 tprefixRemove = VFPU_ANY_SWIZZLE(); - u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 1, 0) | VFPU_CONST(1, 1, 1, 1); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ZERO, VFPUConst::ONE, VFPUConst::ZERO); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), outSize); int n = GetNumVectorElements(sz); @@ -704,7 +720,7 @@ namespace MIPSInt // Not sure who would do this, but using abs/neg allows a compare against 3 or -3. u32 tprefixRemove = VFPU_ANY_SWIZZLE(); - u32 tprefixAdd = VFPU_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(0, 0, 0, 0); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); int n = GetNumVectorElements(sz); @@ -838,8 +854,9 @@ namespace MIPSInt VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); // Swizzle can cause V_Single to properly write both components. - // TODO: Minor, but negate shouldn't apply to invalid here. Maybe always? ApplySwizzleS(s, V_Quad); + // Negate should not actually apply to invalid swizzle. + RetainInvalidSwizzleST(s, V_Quad); VectorSize outsize = V_Single; switch (sz) { @@ -1135,16 +1152,16 @@ namespace MIPSInt u32 sprefixAdd; if (sz == V_Quad) { sprefixRemove = VFPU_SWIZZLE(0, 0, 0, 3); - sprefixAdd = VFPU_CONST(0, 0, 0, 1) | VFPU_SWIZZLE(0, 0, 0, 1); + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::NONE, VFPUConst::NONE, VFPUConst::ONE); } else if (sz == V_Triple) { sprefixRemove = VFPU_SWIZZLE(0, 0, 3, 0); - sprefixAdd = VFPU_CONST(0, 0, 1, 0) | VFPU_SWIZZLE(0, 0, 1, 0); + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::NONE, VFPUConst::ONE, VFPUConst::NONE); } else if (sz == V_Pair) { sprefixRemove = VFPU_SWIZZLE(0, 3, 0, 0); - sprefixAdd = VFPU_CONST(0, 1, 0, 0) | VFPU_SWIZZLE(0, 1, 0, 0); + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::ONE, VFPUConst::NONE, VFPUConst::NONE); } else { sprefixRemove = VFPU_SWIZZLE(3, 0, 0, 0); - sprefixAdd = VFPU_CONST(1, 0, 0, 0) | VFPU_SWIZZLE(1, 0, 0, 0); + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::NONE, VFPUConst::NONE, VFPUConst::NONE); } ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad); @@ -1227,11 +1244,11 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); // TODO: May mishandle NAN / negative zero / etc. - // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. d[0] = std::min(s[0], t[0]); d[1] = std::max(s[1], t[1]); d[2] = std::min(s[2], t[2]); d[3] = std::max(s[3], t[3]); + RetainInvalidSwizzleST(d, sz); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; @@ -1253,11 +1270,11 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); // TODO: May mishandle NAN / negative zero / etc. - // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. d[0] = std::min(s[0], t[0]); d[1] = std::min(s[1], t[1]); d[2] = std::max(s[2], t[2]); d[3] = std::max(s[3], t[3]); + RetainInvalidSwizzleST(d, sz); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; @@ -1279,11 +1296,11 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); // TODO: May mishandle NAN / negative zero / etc. - // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. d[0] = std::max(s[0], t[0]); d[1] = std::min(s[1], t[1]); d[2] = std::max(s[2], t[2]); d[3] = std::min(s[3], t[3]); + RetainInvalidSwizzleST(d, sz); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; @@ -1305,11 +1322,11 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); // TODO: May mishandle NAN / negative zero / etc. - // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. d[0] = std::max(s[0], t[0]); d[1] = std::max(s[1], t[1]); d[2] = std::min(s[2], t[2]); d[3] = std::min(s[3], t[3]); + RetainInvalidSwizzleST(d, sz); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; @@ -1384,7 +1401,7 @@ namespace MIPSInt // T prefix generates constants, but abs can change the constant. u32 tprefixRemove = VFPU_ANY_SWIZZLE(); - u32 tprefixAdd = VFPU_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(1, 1, 1, 1); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); d = 0.0f; @@ -1408,15 +1425,17 @@ namespace MIPSInt // T prefix generates constants, but supports negate. u32 tprefixRemove = VFPU_ANY_SWIZZLE() | VFPU_ABS(1, 1, 1, 1); - u32 tprefixAdd = VFPU_CONST(1, 1, 1, 1); + u32 tprefixAdd; if (sz == V_Single) - tprefixAdd |= VFPU_SWIZZLE(0, 0, 0, 0); + tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO); else if (sz == V_Pair) - tprefixAdd |= VFPU_SWIZZLE(3, 3, 3, 3); + tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::HALF, VFPUConst::HALF, VFPUConst::HALF, VFPUConst::HALF); else if (sz == V_Triple) - tprefixAdd |= VFPU_SWIZZLE(1, 1, 1, 1) | VFPU_ABS(1, 1, 1, 1); + tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::THIRD, VFPUConst::THIRD, VFPUConst::THIRD, VFPUConst::THIRD); else if (sz == V_Quad) - tprefixAdd |= VFPU_SWIZZLE(2, 2, 2, 2) | VFPU_ABS(1, 1, 1, 1); + tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::FOURTH, VFPUConst::FOURTH, VFPUConst::FOURTH, VFPUConst::FOURTH); + else + tprefixAdd = 0; ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); d = 0.0f; @@ -1509,13 +1528,17 @@ namespace MIPSInt float s[4]{}; ReadVector(s, V_Single, vs); u32 sprefixRemove = VFPU_NEGATE(1, 0, 0, 0); - u32 sprefixAdd = VFPU_NEGATE(negSin, 0, 0, 0); - // TODO: Minor, but negate shouldn't apply to invalid here. Maybe always? + // We apply negSin later, not here. This handles zero a bit better. + u32 sprefixAdd = VFPU_NEGATE(0, 0, 0, 0); ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Single); // Cosine ignores all prefixes, so take the original. cosine = vfpu_cos(V(vs)); sine = vfpu_sin(s[0]); + + if (negSin) + sine = -sine; + RetainInvalidSwizzleST(&sine, V_Single); } if (sineLane == cosineLane) { @@ -1562,11 +1585,17 @@ namespace MIPSInt // S and T prefixes apply for the final row only. // The T prefix is used to apply zero/one constants, but abs still changes it. ApplySwizzleS(&s[ins * 4], V_Quad); - u32 tprefixRemove = VFPU_SWIZZLE(0, n < 2 ? 3 : 0, n < 3 ? 3 : 0, n < 4 ? 3 : 0); - u32 tprefixAdd = VFPU_CONST(0, n < 2, n < 3, n < 4); + VFPUConst constX = VFPUConst::NONE; + VFPUConst constY = n < 2 ? VFPUConst::ZERO : VFPUConst::NONE; + VFPUConst constZ = n < 3 ? VFPUConst::ZERO : VFPUConst::NONE; + VFPUConst constW = n < 4 ? VFPUConst::ZERO : VFPUConst::NONE; if (ins >= n) { - tprefixAdd |= VFPU_SWIZZLE(0, ins == 1 ? 1 : 0, ins == 2 ? 1 : 0, ins == 3 ? 1 : 0); + constY = ins == 1 ? VFPUConst::ONE : VFPUConst::ZERO; + constZ = ins == 2 ? VFPUConst::ONE : VFPUConst::ZERO; + constW = ins == 3 ? VFPUConst::ONE : VFPUConst::ZERO; } + u32 tprefixRemove = VFPU_SWIZZLE(0, n < 2 ? 3 : 0, n < 3 ? 3 : 0, n < 4 ? 3 : 0); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); // Really this is the operation all rows probably use (with constant wiring.) @@ -2011,7 +2040,7 @@ namespace MIPSInt d[0] = s[1]*t[2] - s[2]*t[1]; d[1] = s[2]*t[0] - s[0]*t[2]; - // T prefix forces siwzzle and negate, can be used to have weird constants. + // T prefix forces swizzle and negate, can be used to have weird constants. tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2) | VFPU_NEGATE(0, 1, 0, 0); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); ApplySwizzleS(s, V_Quad); @@ -2023,7 +2052,7 @@ namespace MIPSInt d[1] = -s[0]*t[2] + s[1]*t[3] + s[2]*t[0] + s[3]*t[1]; d[2] = s[0]*t[1] - s[1]*t[0] + s[2]*t[3] + s[3]*t[2]; - // T prefix forces siwzzle and negate, can be used to have weird constants. + // T prefix forces swizzle and negate, can be used to have weird constants. tprefixAdd = VFPU_SWIZZLE(0, 1, 2, 3) | VFPU_NEGATE(1, 1, 1, 0); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); ApplySwizzleS(s, sz); @@ -2070,7 +2099,6 @@ namespace MIPSInt VectorSize sz = GetVecSize(op); ReadVector(s.f, sz, vs); - // TODO: Swizzled outside sz stays zero (no -INF.) ApplySwizzleS(s.f, sz); int exp = (s.u[0] & 0x7F800000) >> 23; @@ -2087,14 +2115,13 @@ namespace MIPSInt d.u[i] = s.u[i]; } + RetainInvalidSwizzleST(d.f, sz); ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } - // There has to be a concise way of expressing this in terms of - // bit manipulation on the raw floats. void Int_Vwbn(MIPSOpcode op) { FloatBits d, s; int vd = _VD; @@ -2103,7 +2130,6 @@ namespace MIPSInt u8 exp = (u8)((op >> 16) & 0xFF); ReadVector(s.f, sz, vs); - // TODO: Swizzled outside sz stays zero (no exp insertion.) ApplySwizzleS(s.f, sz); u32 sigbit = s.u[0] & 0x80000000; @@ -2127,6 +2153,7 @@ namespace MIPSInt d.u[i] = s.u[i]; } + RetainInvalidSwizzleST(d.f, sz); ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 97d892ed0b..5a9c59ae70 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -35,7 +35,7 @@ inline int Xpose(int v) { // Some games depend on exact values, but sinf() and cosf() aren't always precise. // Stepping down to [0, 2pi) helps, but we also check common exact-result values. -// TODO: cos(2) and sin(1) should be -0.0, but doing that gives wrong results (possibly from floorf.) +// TODO: cos(1) and sin(2) should be -0.0, but doing that gives wrong results (possibly from floorf.) inline float vfpu_sin(float angle) { angle -= floorf(angle * 0.25f) * 4.f; @@ -145,6 +145,36 @@ static u32 VFPU_NEGATE(int x, int y, int z, int w) { return VFPU_MASK(x, y, z, w) << 16; } +enum class VFPUConst { + NONE = -1, + ZERO, + ONE, + TWO, + HALF, + THREE, + THIRD, + FOURTH, + SIXTH, +}; + +static u32 VFPU_MAKE_CONSTANTS(VFPUConst x, VFPUConst y, VFPUConst z, VFPUConst w) { + u32 result = 0; + if (x != VFPUConst::NONE) { + // This sets the constant flag and the swizzle/abs flags for the right constant. + result |= (((int)x & 3) << 0) | (((int)x & 4) << 6) | (1 << 12); + } + if (y != VFPUConst::NONE) { + result |= (((int)y & 3) << 2) | (((int)y & 4) << 7) | (1 << 13); + } + if (z != VFPUConst::NONE) { + result |= (((int)z & 3) << 4) | (((int)z & 4) << 8) | (1 << 14); + } + if (w != VFPUConst::NONE) { + result |= (((int)w & 3) << 6) | (((int)w & 4) << 9) | (1 << 15); + } + return result; +} + u32 VFPURewritePrefix(int ctrl, u32 remove, u32 add); void ReadMatrix(float *rd, MatrixSize size, int reg); diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 142193a329..018664a00d 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1172,7 +1172,13 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) { break; } } else if (!IsStencilTestOutputDisabled()) { - switch (ReplaceAlphaWithStencilType()) { + StencilValueType stencilValue = ReplaceAlphaWithStencilType(); + if (stencilValue == STENCIL_VALUE_UNIFORM && constantAlpha == 0x00) { + stencilValue = STENCIL_VALUE_ZERO; + } else if (stencilValue == STENCIL_VALUE_UNIFORM && constantAlpha == 0xFF) { + stencilValue = STENCIL_VALUE_ONE; + } + switch (stencilValue) { case STENCIL_VALUE_KEEP: blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ZERO, BlendFactor::ONE); break; diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index a4a6c7e528..13a4acd5cb 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -80,6 +80,7 @@ d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,righ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X, # Linux +050000004c0500006802000000000000,Sony PLAYSTATION(R)3 Controller,platform:Linux,a:b14,b:b13,x:b15,y:b12,back:b0,guide:b16,start:b3,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpdown:b6,dpleft:b7,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a12,righttrigger:a13, 03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, diff --git a/assets/lang b/assets/lang index 86c62b9a23..152c6893e9 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 86c62b9a23936d556ea0899673f7b078a2f12224 +Subproject commit 152c6893e9a30cc45f363cc87a8f48dc5d63cddb diff --git a/build.gradle b/build.gradle index 7b7c94e172..6ac36dd8dc 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.0' } } diff --git a/ffmpeg b/ffmpeg index 7472c903ec..4e8e6a1e7a 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit 7472c903ec771c00af9925097e8d37075e9a379f +Subproject commit 4e8e6a1e7a9ecb4736b4c96f8b3ef1f0e267080b diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 32bb5e0c8d..cd3f2c2a38 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jan 15 23:30:05 CET 2019 +#Fri Apr 19 22:49:10 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index d380f0e09a..86605d55d2 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -188,6 +188,7 @@ static RetroOption ppsspp_separate_io_thread("ppsspp_separate_io_thread", static RetroOption ppsspp_unsafe_func_replacements("ppsspp_unsafe_func_replacements", "Unsafe FuncReplacements", true); static RetroOption ppsspp_sound_speedhack("ppsspp_sound_speedhack", "Sound Speedhack", false); static RetroOption ppsspp_cheats("ppsspp_cheats", "Internal Cheats Support", false); +static RetroOption ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods::IOTIMING_FAST }, { "Host", IOTimingMethods::IOTIMING_HOST }, { "Simulate UMD delays", IOTimingMethods::IOTIMING_REALISTIC } }); void retro_set_environment(retro_environment_t cb) { std::vector vars; @@ -216,6 +217,7 @@ void retro_set_environment(retro_environment_t cb) { vars.push_back(ppsspp_unsafe_func_replacements.GetOptions()); vars.push_back(ppsspp_sound_speedhack.GetOptions()); vars.push_back(ppsspp_cheats.GetOptions()); + vars.push_back(ppsspp_io_timing_method.GetOptions()); vars.push_back({}); environ_cb = cb; @@ -283,6 +285,7 @@ static void check_variables(CoreParameter &coreParam) { ppsspp_locked_cpu_speed.Update(&g_Config.iLockedCPUSpeed); ppsspp_rendering_mode.Update(&g_Config.iRenderingMode); ppsspp_cpu_core.Update((CPUCore *)&g_Config.iCpuCore); + ppsspp_io_timing_method.Update((IOTimingMethods *)&g_Config.iIOTimingMethod); ppsspp_language.Update(&g_Config.iLanguage); if (g_Config.iLanguage < 0) { @@ -453,6 +456,7 @@ void EmuThreadStop() { // Need to keep eating frames to allow the EmuThread to exit correctly. continue; } + emuThread.join(); emuThread = std::thread(); ctx->ThreadEnd(); @@ -462,8 +466,11 @@ void EmuThreadPause() { if (emuThreadState != EmuThreadState::RUNNING) { return; } + emuThreadState = EmuThreadState::PAUSE_REQUESTED; - ctx->ThreadFrame(); + + ctx->ThreadFrame(); // Eat 1 frame + while (emuThreadState != EmuThreadState::PAUSED) { sleep_ms(1); } @@ -675,6 +682,11 @@ void retro_run(void) { retro_input(); if (useEmuThread) { + if(emuThreadState == EmuThreadState::PAUSED || emuThreadState == EmuThreadState::PAUSE_REQUESTED) { + ctx->SwapBuffers(); + return; + } + if (emuThreadState != EmuThreadState::RUNNING) { EmuThreadStart(); } @@ -705,19 +717,48 @@ struct SaveStart { } // namespace SaveState size_t retro_serialize_size(void) { + // TODO: Libretro API extension to use the savestate queue + if (useEmuThread) { + EmuThreadPause(); + } + SaveState::SaveStart state; - return (CChunkFileReader::MeasurePtr(state) + 0x800000) & ~0x7FFFFF; + return (CChunkFileReader::MeasurePtr(state) + 0x800000) & ~0x7FFFFF; // We don't unpause intentionally } bool retro_serialize(void *data, size_t size) { + // TODO: Libretro API extension to use the savestate queue + if (useEmuThread) { + EmuThreadPause(); // Does nothing if already paused + } + SaveState::SaveStart state; assert(CChunkFileReader::MeasurePtr(state) <= size); - return CChunkFileReader::SavePtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; + bool retVal = CChunkFileReader::SavePtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; + + if (useEmuThread) { + EmuThreadStart(); + sleep_ms(4); + } + + return retVal; } bool retro_unserialize(const void *data, size_t size) { + // TODO: Libretro API extension to use the savestate queue + if (useEmuThread) { + EmuThreadPause(); // Does nothing if already paused + } + SaveState::SaveStart state; - return CChunkFileReader::LoadPtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; + bool retVal = CChunkFileReader::LoadPtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; + + if (useEmuThread) { + EmuThreadStart(); + sleep_ms(4); + } + + return retVal; } void *retro_get_memory_data(unsigned id) {