mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
From pspautotests intr/waits: - sceAudioOutputBlocking sets the channel's waiting flag before its event flag wait, and when that wait fails at once (interrupts or dispatch disabled, or inside an interrupt) it returns the error without clearing the flag. The channel stays busy from then on, and can't be released. - The SRC blocking output fails the same way even when a completion is already there, leaving the buffer armed. - After a block that had samples in it, the mixer DMA is still playing it out, so a buffer arriving then isn't read early or restarts it. - sceKernelVolatileMemLock only writes the fake address and size through pointers that are there, instead of faulting on NULL. intr/waits now runs to the end; one scheduling marker still differs, from async IO timing. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
693 lines
26 KiB
C++
693 lines
26 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
#include "Common/Serialize/Serializer.h"
|
|
#include "Common/Serialize/SerializeFuncs.h"
|
|
#include "Core/HLE/HLE.h"
|
|
#include "Core/HLE/FunctionWrappers.h"
|
|
#include "Core/HLE/ErrorCodes.h"
|
|
#include "Core/CoreTiming.h"
|
|
#include "Core/MemMap.h"
|
|
#include "Core/Reporting.h"
|
|
#include "Core/Config.h"
|
|
#include "Core/System.h"
|
|
#include "Core/Compatibility.h"
|
|
#include "Core/Debugger/MemBlockInfo.h"
|
|
|
|
#include "Core/HLE/scePower.h"
|
|
#include "Core/HLE/sceKernelThread.h"
|
|
#include "Core/HLE/sceKernelInterrupt.h"
|
|
|
|
struct VolatileWaitingThread {
|
|
SceUID threadID;
|
|
u32 addrPtr;
|
|
u32 sizePtr;
|
|
};
|
|
|
|
const int PSP_POWER_ERROR_TAKEN_SLOT = 0x80000020;
|
|
const int PSP_POWER_ERROR_SLOTS_FULL = 0x80000022;
|
|
const int PSP_POWER_ERROR_EMPTY_SLOT = 0x80000025;
|
|
const int PSP_POWER_ERROR_INVALID_CB = 0x80000100;
|
|
const int PSP_POWER_ERROR_INVALID_SLOT = 0x80000102;
|
|
|
|
const int PSP_POWER_CB_AC_POWER = 0x00001000;
|
|
const int PSP_POWER_CB_BATTERY_EXIST = 0x00000080;
|
|
const int PSP_POWER_CB_BATTERY_FULL = 0x00000064;
|
|
|
|
const int POWER_CB_AUTO = -1;
|
|
|
|
// These are the callback slots for user mode applications.
|
|
const int numberOfCBPowerSlots = 16;
|
|
|
|
// These are the callback slots for kernel mode applications.
|
|
const int numberOfCBPowerSlotsPrivate = 32;
|
|
|
|
static bool volatileMemLocked;
|
|
static int powerCbSlots[numberOfCBPowerSlots];
|
|
static std::vector<VolatileWaitingThread> volatileWaitingThreads;
|
|
|
|
// Should this belong here, or in CoreTiming?
|
|
static int RealpllFreq = 222000000;
|
|
static int RealbusFreq = 111000000;
|
|
static int pllFreq = 222000000;
|
|
static int busFreq = 111000000;
|
|
|
|
int PowerScaleFromDefaultClock(int us) {
|
|
return (int)((s64)us * 222000000 / (pllFreq > 0 ? pllFreq : 222000000));
|
|
}
|
|
|
|
int GetLockedCPUSpeedMhz() {
|
|
return PSP_CoreParameter().compat.flags().RequireDefaultCPUClock ? 0 : g_Config.iLockedCPUSpeed;
|
|
}
|
|
|
|
// 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 (int)((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));
|
|
|
|
volatileMemLocked = false;
|
|
NotifyMemInfo(MemBlockFlags::ALLOC, 0x08400000, 0x400000, "Volatile memory (not locked)");
|
|
// The tags are functional, because scePower is initialized after sceKernelMemory
|
|
|
|
volatileWaitingThreads.clear();
|
|
|
|
if (GetLockedCPUSpeedMhz() > 0) {
|
|
pllFreq = PowerPllMhzToHz(GetLockedCPUSpeedMhz());
|
|
busFreq = PowerBusMhzToHz(pllFreq / 2000000);
|
|
CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(GetLockedCPUSpeedMhz(), pllFreq));
|
|
} else {
|
|
pllFreq = PowerPllMhzToHz(222);
|
|
busFreq = PowerBusMhzToHz(111);
|
|
}
|
|
RealpllFreq = PowerPllMhzToHz(222);
|
|
RealbusFreq = PowerBusMhzToHz(111);
|
|
}
|
|
|
|
void __PowerDoState(PointerWrap &p) {
|
|
auto s = p.Section("scePower",1,2);
|
|
if (!s)
|
|
return;
|
|
|
|
if (s >= 2) {
|
|
Do(p, RealpllFreq);
|
|
Do(p, RealbusFreq);
|
|
|
|
if (RealpllFreq < 1000000)
|
|
RealpllFreq = PowerPllMhzToHz(RealpllFreq);
|
|
if (RealbusFreq < 1000000)
|
|
RealbusFreq = PowerBusMhzToHz(RealbusFreq);
|
|
} else {
|
|
RealpllFreq = PowerPllMhzToHz(222);
|
|
RealbusFreq = PowerBusMhzToHz(111);
|
|
}
|
|
if (p.mode == p.MODE_READ) {
|
|
if (GetLockedCPUSpeedMhz() > 0) {
|
|
pllFreq = PowerPllMhzToHz(GetLockedCPUSpeedMhz());
|
|
busFreq = PowerBusMhzToHz(pllFreq / 2000000);
|
|
CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(GetLockedCPUSpeedMhz(), pllFreq));
|
|
} else {
|
|
pllFreq = RealpllFreq;
|
|
busFreq = RealbusFreq;
|
|
}
|
|
}
|
|
DoArray(p, powerCbSlots, ARRAY_SIZE(powerCbSlots));
|
|
Do(p, volatileMemLocked);
|
|
Do(p, volatileWaitingThreads);
|
|
}
|
|
|
|
static int scePowerGetBatteryLifePercent() {
|
|
return hleLogDebug(Log::HLE, 100);
|
|
}
|
|
|
|
static int scePowerGetBatteryLifeTime() {
|
|
// 0 means we're on AC power.
|
|
return hleLogDebug(Log::HLE, 0);
|
|
}
|
|
|
|
static int scePowerGetBatteryTemp() {
|
|
// 0 means celsius temperature of the battery
|
|
return hleLogDebug(Log::HLE, 0);
|
|
}
|
|
|
|
static int scePowerIsPowerOnline() {
|
|
return hleLogDebug(Log::HLE, 1);
|
|
}
|
|
|
|
static int scePowerIsBatteryExist() {
|
|
return hleLogDebug(Log::HLE, 1);
|
|
}
|
|
|
|
static int scePowerIsBatteryCharging() {
|
|
return hleLogDebug(Log::HLE, 0);
|
|
}
|
|
|
|
static int scePowerGetBatteryChargingStatus() {
|
|
return hleLogDebug(Log::HLE, 0);
|
|
}
|
|
|
|
static int scePowerIsLowBattery() {
|
|
return hleLogDebug(Log::HLE, 0);
|
|
}
|
|
|
|
static int scePowerIsSuspendRequired() {
|
|
return hleLogDebug(Log::HLE, 0);
|
|
}
|
|
|
|
static int scePowerCancelRequest(u32 something) {
|
|
return hleLogError(Log::sceMisc, 0, "UNIMPL");
|
|
}
|
|
|
|
static int scePowerRequestSuspend() {
|
|
// Don't think we need to do anything.
|
|
return hleLogWarning(Log::sceMisc, 0, "UNIMPL");
|
|
}
|
|
|
|
static int scePowerRequestStandby() {
|
|
// Don't think we need to do anything.
|
|
return hleLogWarning(Log::sceMisc, 0, "UNIMPL");
|
|
}
|
|
|
|
static int scePowerRegisterCallback(int slot, int cbId) {
|
|
if (slot < -1 || slot >= numberOfCBPowerSlotsPrivate) {
|
|
return hleLogError(Log::HLE, PSP_POWER_ERROR_INVALID_SLOT);
|
|
}
|
|
if (slot >= numberOfCBPowerSlots) {
|
|
return hleLogError(Log::HLE, SCE_KERNEL_ERROR_PRIV_REQUIRED);
|
|
}
|
|
// TODO: If cbId is invalid return PSP_POWER_ERROR_INVALID_CB.
|
|
if (cbId == 0) {
|
|
return hleLogError(Log::HLE, PSP_POWER_ERROR_INVALID_CB);
|
|
}
|
|
|
|
int retval = -1;
|
|
|
|
if (slot == POWER_CB_AUTO) { // -1 signifies auto select of bank
|
|
for (int i=0; i < numberOfCBPowerSlots; i++) {
|
|
if (powerCbSlots[i] == 0 && retval == -1) { // found an empty slot
|
|
powerCbSlots[i] = cbId;
|
|
retval = i;
|
|
}
|
|
}
|
|
if (retval == -1) {
|
|
return hleLogError(Log::HLE, PSP_POWER_ERROR_SLOTS_FULL);
|
|
}
|
|
} else {
|
|
if (powerCbSlots[slot] == 0) {
|
|
powerCbSlots[slot] = cbId;
|
|
retval = 0;
|
|
} else {
|
|
return hleLogError(Log::HLE, PSP_POWER_ERROR_TAKEN_SLOT);
|
|
}
|
|
}
|
|
if (retval >= 0) {
|
|
int arg = PSP_POWER_CB_AC_POWER | PSP_POWER_CB_BATTERY_EXIST | PSP_POWER_CB_BATTERY_FULL;
|
|
__KernelNotifyCallback(cbId, arg);
|
|
}
|
|
return hleLogDebugOrError(Log::HLE, retval);
|
|
}
|
|
|
|
static int scePowerUnregisterCallback(int slotId) {
|
|
DEBUG_LOG(Log::HLE, "0=scePowerUnregisterCallback(%i)", slotId);
|
|
|
|
if (slotId < 0 || slotId >= numberOfCBPowerSlotsPrivate) {
|
|
return hleLogError(Log::HLE, PSP_POWER_ERROR_INVALID_SLOT);
|
|
}
|
|
if (slotId >= numberOfCBPowerSlots) {
|
|
return hleLogError(Log::HLE, SCE_KERNEL_ERROR_PRIV_REQUIRED);
|
|
}
|
|
|
|
if (powerCbSlots[slotId] != 0) {
|
|
int cbId = powerCbSlots[slotId];
|
|
powerCbSlots[slotId] = 0;
|
|
return hleLogDebug(Log::HLE, 0, "(cbid = %i)", cbId);
|
|
} else {
|
|
return hleLogError(Log::HLE, PSP_POWER_ERROR_EMPTY_SLOT);
|
|
}
|
|
}
|
|
|
|
static int sceKernelPowerLock(int lockType) {
|
|
if (lockType == 0) {
|
|
return hleLogDebug(Log::HLE, 0);
|
|
} else {
|
|
return hleLogError(Log::HLE, SCE_KERNEL_ERROR_INVALID_MODE);
|
|
}
|
|
}
|
|
|
|
static int sceKernelPowerUnlock(int lockType) {
|
|
if (lockType == 0) {
|
|
return hleLogDebug(Log::HLE, 0);
|
|
} else {
|
|
return hleLogError(Log::HLE, SCE_KERNEL_ERROR_INVALID_MODE);
|
|
}
|
|
}
|
|
|
|
static int sceKernelPowerTick(int flag) {
|
|
return hleLogDebug(Log::HLE, 0, "UNIMPL");
|
|
}
|
|
|
|
// not a syscall
|
|
int KernelVolatileMemLock(int type, u32 paddr, u32 psize) {
|
|
if (type != 0) {
|
|
return SCE_KERNEL_ERROR_INVALID_MODE;
|
|
}
|
|
if (volatileMemLocked) {
|
|
return SCE_KERNEL_ERROR_POWER_VMEM_IN_USE;
|
|
}
|
|
|
|
// Volatile RAM is always at 0x08400000 and is of size 0x00400000.
|
|
// It's always available in the emu.
|
|
// TODO: Should really reserve this properly!
|
|
if (Memory::IsValidAddress(paddr)) {
|
|
Memory::WriteUnchecked_U32(0x08400000, paddr);
|
|
}
|
|
if (Memory::IsValidAddress(psize)) {
|
|
Memory::WriteUnchecked_U32(0x00400000, psize);
|
|
}
|
|
volatileMemLocked = true;
|
|
NotifyMemInfo(MemBlockFlags::ALLOC, 0x08400000, 0x400000, "Volatile memory (locked)");
|
|
return 0;
|
|
}
|
|
|
|
static int sceKernelVolatileMemTryLock(int type, u32 paddr, u32 psize) {
|
|
u32 error = KernelVolatileMemLock(type, paddr, psize);
|
|
|
|
switch (error) {
|
|
case 0:
|
|
// HACK: This fixes Crash Tag Team Racing.
|
|
// Should only wait 1200 cycles though according to Unknown's testing,
|
|
// and with that it's still broken. So it's not this, unfortunately.
|
|
// Leaving it in for the 0.9.8 release anyway.
|
|
hleEatCycles(500000);
|
|
DEBUG_LOG(Log::HLE, "sceKernelVolatileMemTryLock(%i, %08x, %08x) - success", type, paddr, psize);
|
|
break;
|
|
|
|
case SCE_KERNEL_ERROR_POWER_VMEM_IN_USE:
|
|
// This is OK, let's not ERROR_LOG.
|
|
return hleLogDebug(Log::HLE, error, "(%i, %08x, %08x) - already locked!", type, paddr, psize);
|
|
|
|
default:
|
|
ERROR_LOG_REPORT(Log::HLE, "%08x=sceKernelVolatileMemTryLock(%i, %08x, %08x) - error", type, paddr, psize, error);
|
|
break;
|
|
}
|
|
|
|
return hleLogDebugOrError(Log::HLE, error);
|
|
}
|
|
|
|
int KernelVolatileMemUnlock(int type) {
|
|
if (type != 0) {
|
|
return SCE_KERNEL_ERROR_INVALID_MODE;
|
|
}
|
|
if (!volatileMemLocked) {
|
|
// I guess it must use a sema.
|
|
return SCE_KERNEL_ERROR_SEMA_OVF;
|
|
}
|
|
|
|
volatileMemLocked = false;
|
|
NotifyMemInfo(MemBlockFlags::ALLOC, 0x08400000, 0x400000, "Volatile memory (not locked)");
|
|
|
|
// Wake someone, always fifo.
|
|
bool wokeThreads = false;
|
|
u32 error;
|
|
while (!volatileWaitingThreads.empty() && !volatileMemLocked) {
|
|
VolatileWaitingThread waitInfo = volatileWaitingThreads.front();
|
|
volatileWaitingThreads.erase(volatileWaitingThreads.begin());
|
|
|
|
int waitID = __KernelGetWaitID(waitInfo.threadID, WAITTYPE_VMEM, error);
|
|
// If they were force-released, just skip.
|
|
if (waitID == 1 && KernelVolatileMemLock(0, waitInfo.addrPtr, waitInfo.sizePtr) == 0) {
|
|
__KernelResumeThreadFromWait(waitInfo.threadID, 0);
|
|
wokeThreads = true;
|
|
}
|
|
}
|
|
|
|
if (wokeThreads) {
|
|
INFO_LOG(Log::HLE, "KernelVolatileMemUnlock(%i) handed over to another thread", type);
|
|
hleReSchedule("volatile mem unlocked");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sceKernelVolatileMemUnlock(int type) {
|
|
int error = KernelVolatileMemUnlock(type);
|
|
if (error == SCE_KERNEL_ERROR_INVALID_MODE) {
|
|
ERROR_LOG_REPORT(Log::HLE, "sceKernelVolatileMemUnlock(%i) - invalid mode", type);
|
|
return error;
|
|
} else if (error == SCE_KERNEL_ERROR_SEMA_OVF) {
|
|
ERROR_LOG_REPORT(Log::HLE, "sceKernelVolatileMemUnlock(%i) FAILED - not locked", type);
|
|
return error;
|
|
}
|
|
|
|
return hleLogDebug(Log::HLE, 0);
|
|
}
|
|
|
|
static int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) {
|
|
u32 error = 0;
|
|
|
|
// If dispatch is disabled or in an interrupt, don't check, just return an error.
|
|
// But still write the addr and size (some games require this to work, and it's testably true.)
|
|
if (!__KernelIsDispatchEnabled()) {
|
|
error = SCE_KERNEL_ERROR_CAN_NOT_WAIT;
|
|
} else if (__IsInInterrupt()) {
|
|
error = SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
|
|
} else {
|
|
error = KernelVolatileMemLock(type, paddr, psize);
|
|
}
|
|
|
|
switch (error) {
|
|
case 0:
|
|
// Should only wait 1200 cycles though according to Unknown's testing,
|
|
hleEatCycles(1200);
|
|
DEBUG_LOG(Log::HLE, "sceKernelVolatileMemLock(%i, %08x, %08x) - success", type, paddr, psize);
|
|
break;
|
|
|
|
case SCE_KERNEL_ERROR_POWER_VMEM_IN_USE:
|
|
{
|
|
WARN_LOG(Log::HLE, "sceKernelVolatileMemLock(%i, %08x, %08x) - already locked, waiting", type, paddr, psize);
|
|
const VolatileWaitingThread waitInfo = { __KernelGetCurThread(), paddr, psize };
|
|
volatileWaitingThreads.push_back(waitInfo);
|
|
__KernelWaitCurThread(WAITTYPE_VMEM, 1, 0, 0, false, "volatile mem waited");
|
|
}
|
|
break;
|
|
|
|
case SCE_KERNEL_ERROR_CAN_NOT_WAIT:
|
|
{
|
|
WARN_LOG(Log::HLE, "sceKernelVolatileMemLock(%i, %08x, %08x): dispatch disabled", type, paddr, psize);
|
|
// Only through pointers that are there: intr/waits passes NULL and gets just the error.
|
|
if (Memory::IsValid4AlignedAddress(paddr))
|
|
Memory::WriteUnchecked_U32(0x08400000, paddr);
|
|
if (Memory::IsValid4AlignedAddress(psize))
|
|
Memory::WriteUnchecked_U32(0x00400000, psize);
|
|
}
|
|
break;
|
|
|
|
case SCE_KERNEL_ERROR_ILLEGAL_CONTEXT:
|
|
{
|
|
WARN_LOG(Log::HLE, "sceKernelVolatileMemLock(%i, %08x, %08x): in interrupt", type, paddr, psize);
|
|
// Only through pointers that are there: intr/waits passes NULL and gets just the error.
|
|
if (Memory::IsValid4AlignedAddress(paddr))
|
|
Memory::WriteUnchecked_U32(0x08400000, paddr);
|
|
if (Memory::IsValid4AlignedAddress(psize))
|
|
Memory::WriteUnchecked_U32(0x00400000, psize);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_LOG_REPORT(Log::HLE, "%08x=sceKernelVolatileMemLock(%i, %08x, %08x) - error", type, paddr, psize, error);
|
|
break;
|
|
}
|
|
|
|
return hleNoLog(error);
|
|
}
|
|
|
|
|
|
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(Log::sceMisc, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid pll frequency");
|
|
}
|
|
if (cpufreq == 0 || cpufreq > 333) {
|
|
return hleLogWarning(Log::sceMisc, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid cpu frequency");
|
|
}
|
|
if (busfreq == 0 || busfreq > 166) {
|
|
return hleLogWarning(Log::sceMisc, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid bus frequency");
|
|
}
|
|
// 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 (GetLockedCPUSpeedMhz() <= 0) {
|
|
pllFreq = RealpllFreq;
|
|
busFreq = RealbusFreq;
|
|
CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq));
|
|
}
|
|
|
|
// 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(hleNoLog(0), "scepower set clockFrequency", usec);
|
|
}
|
|
if (GetLockedCPUSpeedMhz() <= 0) {
|
|
if (CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq))) {
|
|
return hleLogInfo(Log::HLE, 0);
|
|
} else {
|
|
return hleLogDebug(Log::HLE, 0);
|
|
}
|
|
} else {
|
|
return hleLogInfo(Log::HLE, 0, "locked by user config at %i, %i, %i", GetLockedCPUSpeedMhz(), GetLockedCPUSpeedMhz(), busFreq);
|
|
}
|
|
return hleNoLog(0);
|
|
}
|
|
|
|
static u32 scePowerSetCpuClockFrequency(u32 cpufreq) {
|
|
if (cpufreq == 0 || cpufreq > 333) {
|
|
return hleLogWarning(Log::sceMisc, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency");
|
|
}
|
|
if (GetLockedCPUSpeedMhz() > 0) {
|
|
return hleLogDebug(Log::sceMisc, 0, "locked by user config at %i", GetLockedCPUSpeedMhz());
|
|
}
|
|
CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq));
|
|
return hleLogDebug(Log::sceMisc, 0);
|
|
}
|
|
|
|
static u32 scePowerSetBusClockFrequency(u32 busfreq) {
|
|
if (busfreq == 0 || busfreq > 111) {
|
|
return hleLogWarning(Log::sceMisc, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency");
|
|
}
|
|
if (GetLockedCPUSpeedMhz() > 0) {
|
|
return hleLogDebug(Log::sceMisc, 0, "locked by user config at %i", GetLockedCPUSpeedMhz() / 2);
|
|
}
|
|
|
|
// 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 hleLogDebug(Log::sceMisc, 0);
|
|
}
|
|
|
|
static u32 scePowerGetCpuClockFrequencyInt() {
|
|
int cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000;
|
|
return hleLogDebug(Log::sceMisc, cpuFreq);
|
|
}
|
|
|
|
static u32 scePowerGetPllClockFrequencyInt() {
|
|
return hleLogDebug(Log::sceMisc, pllFreq / 1000000);
|
|
}
|
|
|
|
static u32 scePowerGetBusClockFrequencyInt() {
|
|
return hleLogDebug(Log::sceMisc, busFreq / 1000000);
|
|
}
|
|
|
|
static float scePowerGetCpuClockFrequencyFloat() {
|
|
float cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000.0f;
|
|
DEBUG_LOG(Log::sceMisc, "%f=scePowerGetCpuClockFrequencyFloat()", (float)cpuFreq);
|
|
return cpuFreq;
|
|
}
|
|
|
|
static float scePowerGetPllClockFrequencyFloat() {
|
|
INFO_LOG(Log::sceMisc, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq / 1000000.0f);
|
|
return (float) pllFreq / 1000000.0f;
|
|
}
|
|
|
|
static float scePowerGetBusClockFrequencyFloat() {
|
|
INFO_LOG(Log::sceMisc, "%f=scePowerGetBusClockFrequencyFloat()", (float)busFreq / 1000000.0f);
|
|
return (float) busFreq / 1000000.0f;
|
|
}
|
|
|
|
static int scePowerTick() {
|
|
// Don't think we need to do anything.
|
|
return hleLogDebug(Log::sceMisc, 0);
|
|
}
|
|
|
|
static u32 scePowerCheckWlanCoexistenceClock() {
|
|
// PSP-1000 vs PSP-2000/3000
|
|
return hleLogDebug(Log::sceMisc, g_Config.iPSPModel);
|
|
}
|
|
|
|
static const HLEFunction scePower[] = {
|
|
{0X04B7766E, &WrapI_II<scePowerRegisterCallback>, "scePowerRegisterCallback", 'i', "ii" },
|
|
{0X2B51FE2F, nullptr, "scePower_2B51FE2F", '?', "" },
|
|
{0X442BFBAC, nullptr, "scePowerGetBacklightMaximum", '?', "" },
|
|
{0XEFD3C963, &WrapI_V<scePowerTick>, "scePowerTick", 'i', "" },
|
|
{0XEDC13FE5, nullptr, "scePowerGetIdleTimer", '?', "" },
|
|
{0X7F30B3B1, nullptr, "scePowerIdleTimerEnable", '?', "" },
|
|
{0X972CE941, nullptr, "scePowerIdleTimerDisable", '?', "" },
|
|
{0X27F3292C, nullptr, "scePowerBatteryUpdateInfo", '?', "" },
|
|
{0XE8E4E204, nullptr, "scePowerGetForceSuspendCapacity", '?', "" },
|
|
{0XB999184C, nullptr, "scePowerGetLowBatteryCapacity", '?', "" },
|
|
{0X87440F5E, &WrapI_V<scePowerIsPowerOnline>, "scePowerIsPowerOnline", 'i', "" },
|
|
{0X0AFD0D8B, &WrapI_V<scePowerIsBatteryExist>, "scePowerIsBatteryExist", 'i', "" },
|
|
{0X1E490401, &WrapI_V<scePowerIsBatteryCharging>, "scePowerIsBatteryCharging", 'i', "" },
|
|
{0XB4432BC8, &WrapI_V<scePowerGetBatteryChargingStatus>, "scePowerGetBatteryChargingStatus", 'i', "" },
|
|
{0XD3075926, &WrapI_V<scePowerIsLowBattery>, "scePowerIsLowBattery", 'i', "" },
|
|
{0X78A1A796, &WrapI_V<scePowerIsSuspendRequired>, "scePowerIsSuspendRequired", '?', "" },
|
|
{0X94F5A53F, nullptr, "scePowerGetBatteryRemainCapacity", '?', "" },
|
|
{0XFD18A0FF, nullptr, "scePowerGetBatteryFullCapacity", '?', "" },
|
|
{0X2085D15D, &WrapI_V<scePowerGetBatteryLifePercent>, "scePowerGetBatteryLifePercent", 'i', "" },
|
|
{0X8EFB3FA2, &WrapI_V<scePowerGetBatteryLifeTime>, "scePowerGetBatteryLifeTime", 'i', "" },
|
|
{0X28E12023, &WrapI_V<scePowerGetBatteryTemp>, "scePowerGetBatteryTemp", 'i', "" },
|
|
{0X862AE1A6, nullptr, "scePowerGetBatteryElec", '?', "" },
|
|
{0X483CE86B, nullptr, "scePowerGetBatteryVolt", '?', "" },
|
|
{0XCB49F5CE, nullptr, "scePowerGetBatteryChargeCycle", '?', "" },
|
|
{0X23436A4A, nullptr, "scePowerGetInnerTemp", '?', "" },
|
|
{0X0CD21B1F, nullptr, "scePowerSetPowerSwMode", '?', "" },
|
|
{0X165CE085, nullptr, "scePowerGetPowerSwMode", '?', "" },
|
|
{0XD6D016EF, nullptr, "scePowerLock", '?', "" },
|
|
{0XCA3D34C1, nullptr, "scePowerUnlock", '?', "" },
|
|
{0XDB62C9CF, &WrapI_U<scePowerCancelRequest>, "scePowerCancelRequest", 'i', "" },
|
|
{0X7FA406DD, nullptr, "scePowerIsRequest", '?', "" },
|
|
{0X2B7C7CF4, &WrapI_V<scePowerRequestStandby>, "scePowerRequestStandby", 'I', "" },
|
|
{0XAC32C9CC, &WrapI_V<scePowerRequestSuspend>, "scePowerRequestSuspend", 'I', "" },
|
|
{0X2875994B, nullptr, "scePower_2875994B", '?', "" },
|
|
{0X0074EF9B, nullptr, "scePowerGetResumeCount", '?', "" },
|
|
{0XDFA8BAF8, &WrapI_I<scePowerUnregisterCallback>, "scePowerUnregisterCallback", 'i', "i" },
|
|
{0XDB9D28DD, &WrapI_I<scePowerUnregisterCallback>, "scePowerUnregitserCallback", 'i', "i" },
|
|
{0X843FBF43, &WrapU_U<scePowerSetCpuClockFrequency>, "scePowerSetCpuClockFrequency", 'x', "x" },
|
|
{0XB8D7B3FB, &WrapU_U<scePowerSetBusClockFrequency>, "scePowerSetBusClockFrequency", 'x', "x" },
|
|
{0XFEE03A2F, &WrapU_V<scePowerGetCpuClockFrequencyInt>, "scePowerGetCpuClockFrequency", 'x', "" },
|
|
{0X478FE6F5, &WrapU_V<scePowerGetBusClockFrequencyInt>, "scePowerGetBusClockFrequency", 'x', "" },
|
|
{0XFDB5BFE9, &WrapU_V<scePowerGetCpuClockFrequencyInt>, "scePowerGetCpuClockFrequencyInt", 'x', "" },
|
|
{0XBD681969, &WrapU_V<scePowerGetBusClockFrequencyInt>, "scePowerGetBusClockFrequencyInt", 'x', "" },
|
|
{0XB1A52C83, &WrapF_V<scePowerGetCpuClockFrequencyFloat>, "scePowerGetCpuClockFrequencyFloat", 'f', "" },
|
|
{0X9BADB3EB, &WrapF_V<scePowerGetBusClockFrequencyFloat>, "scePowerGetBusClockFrequencyFloat", 'f', "" },
|
|
{0X737486F2, &WrapU_UUU<scePowerSetClockFrequency>, "scePowerSetClockFrequency", 'x', "xxx"},
|
|
{0X34F9C463, &WrapU_V<scePowerGetPllClockFrequencyInt>, "scePowerGetPllClockFrequencyInt", 'x', "" },
|
|
{0XEA382A27, &WrapF_V<scePowerGetPllClockFrequencyFloat>, "scePowerGetPllClockFrequencyFloat", 'f', "" },
|
|
{0XEBD177D6, &WrapU_UUU<scePowerSetClockFrequency>, "scePowerSetClockFrequency350", 'x', "xxx"}, // This is also the same as SetClockFrequency
|
|
{0X469989AD, &WrapU_UUU<scePowerSetClockFrequency>, "scePowerSetClockFrequency630", 'x', "xxx"}, // This is also the same as SetClockFrequency
|
|
{0X545A7F3C, nullptr, "scePower_545A7F3C", '?', "" }, // TODO: Supposedly the same as SetClockFrequency also?
|
|
{0XA4E93389, nullptr, "scePower_A4E93389", '?', "" }, // TODO: Supposedly the same as SetClockFrequency also?
|
|
{0XA85880D0, &WrapU_V<scePowerCheckWlanCoexistenceClock>, "scePowerCheckWlanCoexistenceClock", 'x', "" },
|
|
{0X3951AF53, nullptr, "scePowerWaitRequestCompletion", '?', "" },
|
|
{0X0442D852, nullptr, "scePowerRequestColdReset", '?', "" },
|
|
{0XBAFA3DF0, nullptr, "scePowerGetCallbackMode", '?', "" },
|
|
{0XA9D22232, nullptr, "scePowerSetCallbackMode", '?', "" },
|
|
|
|
// These seem to be aliases.
|
|
{0X23C31FFE, &WrapI_IUU<sceKernelVolatileMemLock>, "scePowerVolatileMemLock", 'i', "ixx"},
|
|
{0XFA97A599, &WrapI_IUU<sceKernelVolatileMemTryLock>, "scePowerVolatileMemTryLock", 'i', "ixx"},
|
|
{0XB3EDD801, &WrapI_I<sceKernelVolatileMemUnlock>, "scePowerVolatileMemUnlock", 'i', "i" },
|
|
};
|
|
|
|
//890129c in tyshooter looks bogus
|
|
const HLEFunction sceSuspendForUser[] = {
|
|
{0XEADB1BD7, &WrapI_I<sceKernelPowerLock>, "sceKernelPowerLock", 'i', "i" }, //(int param) set param to 0
|
|
{0X3AEE7261, &WrapI_I<sceKernelPowerUnlock>, "sceKernelPowerUnlock", 'i', "i" }, //(int param) set param to 0
|
|
{0X090CCB3F, &WrapI_I<sceKernelPowerTick>, "sceKernelPowerTick", 'i', "i" },
|
|
|
|
// There's an extra 4MB that can be allocated, which seems to be "volatile". These functions
|
|
// let you grab it.
|
|
{0XA14F40B2, &WrapI_IUU<sceKernelVolatileMemTryLock>, "sceKernelVolatileMemTryLock", 'i', "ixx"},
|
|
{0XA569E425, &WrapI_I<sceKernelVolatileMemUnlock>, "sceKernelVolatileMemUnlock", 'i', "i" },
|
|
{0X3E0271D3, &WrapI_IUU<sceKernelVolatileMemLock>, "sceKernelVolatileMemLock", 'i', "ixx"},
|
|
};
|
|
|
|
|
|
void Register_scePower() {
|
|
RegisterHLEModule("scePower",ARRAY_SIZE(scePower),scePower);
|
|
}
|
|
|
|
// Real name unknown (jpcsp's scePower.java doesn't have one either - it's named after its
|
|
// own NID, scePower_driver_5F5006D2, and just returns 0 unconditionally). Kernel-only alias
|
|
// module some firmware-660+ code (e.g. the VSH's sceVshBridge_Driver) imports from.
|
|
static int scePower_driver_5F5006D2() {
|
|
return hleLogDebug(Log::HLE, 0, "UNTESTED");
|
|
}
|
|
|
|
// Configures which events would wake the console from suspend. We never suspend, so there's
|
|
// nothing to arm - but the VSH calls it during startup and wants a success back.
|
|
static int scePowerSetWakeupCondition(u32 condition) {
|
|
return hleLogWarning(Log::sceMisc, 0, "UNIMPL");
|
|
}
|
|
|
|
const HLEFunction scePower_driver[] = {
|
|
{0X5F5006D2, &WrapI_V<scePower_driver_5F5006D2>, "scePower_driver_5F5006D2", 'i', "" },
|
|
{0XBA566CD0, &WrapI_U<scePowerSetWakeupCondition>, "scePowerSetWakeupCondition", 'i', "x" },
|
|
};
|
|
|
|
void Register_scePower_driver() {
|
|
RegisterHLEModule("scePower_driver", ARRAY_SIZE(scePower_driver), scePower_driver);
|
|
}
|
|
|
|
void Register_sceSuspendForUser() {
|
|
RegisterHLEModule("sceSuspendForUser", ARRAY_SIZE(sceSuspendForUser), sceSuspendForUser);
|
|
}
|