diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 22eb1f25cd..7ac7a64719 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -37,6 +37,9 @@
{75f0ee2b-1397-4cf3-9960-94ccd7d4d803}
+
+ {9696662f-7398-489a-a358-5ebbf4ad4d97}
+
@@ -147,9 +150,6 @@
HLE\Kernel
-
- HLE\Kernel
-
HLE\Kernel
@@ -285,6 +285,9 @@
ELF
+
+ HW
+
@@ -377,9 +380,6 @@
HLE\Kernel
-
- HLE\Kernel
-
HLE\Kernel
@@ -521,6 +521,9 @@
ELF
+
+ HW
+
diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp
index 3baa309438..61c8f60856 100644
--- a/Core/FileSystems/DirectoryFileSystem.cpp
+++ b/Core/FileSystems/DirectoryFileSystem.cpp
@@ -81,7 +81,7 @@ bool DirectoryFileSystem::DeleteFile(const std::string &filename)
u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access)
{
std::string fullName = GetLocalPath(filename);
- INFO_LOG(HLE,"Actually opening %s",fullName.c_str());
+ INFO_LOG(HLE,"Actually opening %s (%s)", fullName.c_str(), filename.c_str());
OpenFileEntry entry;
diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp
index 4c7279a2da..3dd8cb35d4 100644
--- a/Core/FileSystems/MetaFileSystem.cpp
+++ b/Core/FileSystems/MetaFileSystem.cpp
@@ -94,6 +94,11 @@ u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access)
PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename)
{
std::string of;
+ if (filename.find(':') == std::string::npos)
+ {
+ filename = currentDirectory + "/" + filename;
+ DEBUG_LOG(HLE,"GetFileInfo: Expanded path to %s", filename.c_str());
+ }
IFileSystem *system;
if (MapFilePath(filename, of, &system))
{
diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp
index 163d1cbc4f..4be48dace0 100644
--- a/Core/HLE/HLE.cpp
+++ b/Core/HLE/HLE.cpp
@@ -198,4 +198,3 @@ void CallSyscall(u32 op)
ERROR_LOG(HLE,"Unimplemented HLE function %s", moduleDB[modulenum].funcTable[funcnum].name);
}
}
-
diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp
index 985e79fafd..9cee2dddc9 100644
--- a/Core/HLE/HLETables.cpp
+++ b/Core/HLE/HLETables.cpp
@@ -36,7 +36,6 @@
#include "sceDmac.h"
#include "sceKernel.h"
#include "sceKernelEventFlag.h"
-#include "sceKernelCallback.h"
#include "sceKernelMemory.h"
#include "sceKernelInterrupt.h"
#include "sceKernelModule.h"
diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp
index 869a95e278..c1b51a3a90 100644
--- a/Core/HLE/sceDisplay.cpp
+++ b/Core/HLE/sceDisplay.cpp
@@ -220,28 +220,29 @@ void sceDisplaySetFramebuf()
void sceDisplayGetFramebuf()
{
- DEBUG_LOG(HLE,"sceDisplayGetFramebuf");
+ DEBUG_LOG(HLE,"sceDisplayGetFramebuf()");
RETURN(framebuf.topaddr);
}
void sceDisplayWaitVblankStart()
{
- DEBUG_LOG(HLE,"sceDisplayWaitVblankStart");
+ DEBUG_LOG(HLE,"sceDisplayWaitVblankStart()");
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false);
RETURN(0);
}
void sceDisplayWaitVblank()
{
- DEBUG_LOG(HLE,"sceDisplayWaitVblank");
+ DEBUG_LOG(HLE,"sceDisplayWaitVblank()");
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false);
sceDisplayWaitVblankStart();
}
void sceDisplayWaitVblankStartCB()
{
- DEBUG_LOG(HLE,"sceDisplayWaitVblankStartCB");
+ DEBUG_LOG(HLE,"sceDisplayWaitVblankStartCB()");
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true);
+ __KernelCheckCallbacks();
}
void sceDisplayGetVcount()
diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp
index 680e6b822d..50ed1e2e1d 100644
--- a/Core/HLE/sceGe.cpp
+++ b/Core/HLE/sceGe.cpp
@@ -20,7 +20,7 @@
#include "../System.h"
#include "../CoreParameter.h"
#include "sceGe.h"
-#include "sceKernelCallback.h"
+#include "sceKernelThread.h"
#include "sceKernelInterrupt.h"
// TODO: Bad dependency.
@@ -114,7 +114,7 @@ u32 sceGeDrawSync(u32)
{
//wait/check entire drawing state
u32 mode = PARAM(0); //0 : wait for completion 1:check and return
- DEBUG_LOG(HLE,"FAKE sceGeDrawSync(mode=%d)",mode);
+ DEBUG_LOG(HLE,"FAKE sceGeDrawSync(mode=%d) (0=wait for completion)",mode);
if (mode == 1)
{
return 0;
diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp
index f4d4224c2c..9b689f6003 100644
--- a/Core/HLE/sceIo.cpp
+++ b/Core/HLE/sceIo.cpp
@@ -23,6 +23,7 @@
#include "../System.h"
#include "HLE.h"
#include "../MIPS/MIPS.h"
+#include "../HW/MemoryStick.h"
#include "../FileSystems/FileSystem.h"
#include "../FileSystems/MetaFileSystem.h"
@@ -36,6 +37,10 @@
#define ERROR_ERRNO_FILE_NOT_FOUND 0x80010002
+
+#define ERROR_MEMSTICK_DEVCTL_BAD_PARAMS 0x80220081
+#define ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS 0x80220082
+
/*
TODO: async io is missing features!
@@ -178,10 +183,10 @@ void __IoInit()
memstick = new DirectoryFileSystem(&pspFileSystem, mypath);
pspFileSystem.Mount("ms0:", memstick);
- pspFileSystem.Mount("fatms0:", memstick);
- pspFileSystem.Mount("fatms:", memstick);
- pspFileSystem.Mount("flash0:", new EmptyFileSystem());
- pspFileSystem.Mount("flash1:", new EmptyFileSystem());
+ pspFileSystem.Mount("fatms0:", memstick);
+ pspFileSystem.Mount("fatms:", memstick);
+ pspFileSystem.Mount("flash0:", new EmptyFileSystem());
+ pspFileSystem.Mount("flash1:", new EmptyFileSystem());
}
void __IoShutdown()
@@ -224,7 +229,7 @@ void __IoCompleteAsyncIO(SceUID id)
{
if (f->callbackID)
{
- __KernelNotifyCallback(__KernelGetCurThread(), f->callbackID, f->callbackArg);
+ // __KernelNotifyCallbackType(THREAD_CALLBACK_IO, __KernelGetCurThread(), f->callbackID, f->callbackArg);
}
}
}
@@ -476,10 +481,32 @@ void sceIoDevctl() //(const char *name, int cmd, void *arg, size_t arglen, void
int argLen = PARAM(3);
u32 outPtr = PARAM(4);
int outLen = PARAM(5);
- int retVal = 0;
- DEBUG_LOG(HLE,"%i=sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)",
- retVal, name, cmd,argAddr,argLen,outPtr,outLen);
+ DEBUG_LOG(HLE,"sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)",
+ name, cmd,argAddr,argLen,outPtr,outLen);
+ // UMD checks
+ switch (cmd) {
+ case 0x01F20001: // Get Disc Type.
+ if (Memory::IsValidAddress(outPtr)) {
+ Memory::Write_U32(0x10, outPtr); // Game disc
+ RETURN(0); return;
+ } else {
+ RETURN(-1); return;
+ }
+ break;
+ case 0x01F20002: // Get current LBA.
+ if (Memory::IsValidAddress(outPtr)) {
+ Memory::Write_U32(0, outPtr); // Game disc
+ RETURN(0); return;
+ } else {
+ RETURN(-1); return;
+ }
+ break;
+ case 0x01F100A3: // Seek
+ RETURN(0); return;
+ break;
+ }
+
// This should really send it on to a FileSystem implementation instead.
if (!strcmp(name, "mscmhc0:"))
@@ -487,14 +514,48 @@ void sceIoDevctl() //(const char *name, int cmd, void *arg, size_t arglen, void
switch (cmd)
{
// does one of these set a callback as well? (see coded arms)
+ case 0x02025804: // Register callback
+ if (Memory::IsValidAddress(argAddr) && argLen == 4) {
+ u32 cbId = Memory::Read_U32(argAddr);
+ if (0 == __KernelRegisterCallback(THREAD_CALLBACK_MEMORYSTICK, cbId)) {
+ DEBUG_LOG(HLE, "sceIoDevCtl: Memstick callback %i registered, notifying immediately.", cbId);
+ __KernelNotifyCallbackType(THREAD_CALLBACK_MEMORYSTICK, cbId, MemoryStick_State());
+ RETURN(0);
+ } else {
+ RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
+ }
+ return;
+ }
+ break;
+
+ case 0x02025805: // Unregister callback
+ if (Memory::IsValidAddress(argAddr) && argLen == 4) {
+ u32 cbId = Memory::Read_U32(argAddr);
+ if (0 == __KernelUnregisterCallback(THREAD_CALLBACK_MEMORYSTICK, cbId)) {
+ DEBUG_LOG(HLE, "sceIoDevCtl: Unregistered memstick callback %i", cbId);
+ RETURN(0);
+ } else {
+ RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
+ }
+ return;
+ }
+ break;
+
case 0x02025806: // Memory stick inserted?
case 0x02025801: // Memstick Driver status?
- Memory::Write_U32(1, outPtr);
- RETURN(0);
+ if (Memory::IsValidAddress(outPtr)) {
+ Memory::Write_U32(1, outPtr);
+ RETURN(0);
+ } else {
+ RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
+ }
return;
- case 0x02425818:
+
+ case 0x02425818: // Get memstick size etc
// Pretend we have a 2GB memory stick.
- {
+ if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't
+ u32 pointer = Memory::Read_U32(argAddr);
+
u64 totalSize = (u32)2 * 1024 * 1024 * 1024;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
@@ -503,36 +564,68 @@ void sceIoDevctl() //(const char *name, int cmd, void *arg, size_t arglen, void
deviceSize.sectorsPerCluster = 0x08;
deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
- Memory::WriteStruct(outPtr, &deviceSize);
+ Memory::WriteStruct(pointer, &deviceSize);
RETURN(0);
- return;
+ } else {
+ RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
}
-
+ return;
}
}
if (!strcmp(name, "fatms0:"))
{
switch (cmd) {
- case 0x02425823:
- if (Memory::IsValidAddress(outPtr))
- Memory::Write_U32(1, outPtr); // TODO: Make a headless mode for running tests!
- break;
case 0x02415821: // MScmRegisterMSInsertEjectCallback
{
u32 cbId = Memory::Read_U32(argAddr);
- ERROR_LOG(HLE, "sceIoDevCtl: Registering memstick callbacks not yet supported (%08x)", cbId);
+ if (0 == __KernelRegisterCallback(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId)) {
+ DEBUG_LOG(HLE, "sceIoDevCtl: Memstick FAT callback %i registered, notifying immediately.", cbId);
+ __KernelNotifyCallbackType(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId, MemoryStick_FatState());
+ RETURN(0);
+ } else {
+ RETURN(-1);
+ }
+ return;
}
break;
case 0x02415822: // MScmUnregisterMSInsertEjectCallback
{
u32 cbId = Memory::Read_U32(argAddr);
- ERROR_LOG(HLE, "sceIoDevCtl: Unregistering memstick callbacks not yet supported (%08x)", cbId);
+ if (0 == __KernelUnregisterCallback(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId)) {
+ DEBUG_LOG(HLE, "sceIoDevCtl: Unregistered memstick FAT callback %i", cbId);
+ RETURN(0);
+ } else {
+ RETURN(-1);
+ }
+ return;
}
+ case 0x02425823: // Check if valid
+ if (Memory::IsValidAddress(outPtr))
+ Memory::Write_U32(1, outPtr); // TODO: Make a headless mode for running tests!
break;
+ case 0x02425818: // Get memstick size etc
+ // Pretend we have a 2GB memory stick.
+ {
+ if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't
+ u32 pointer = Memory::Read_U32(argAddr);
+
+ u64 totalSize = (u32)2 * 1024 * 1024 * 1024;
+ u64 freeSize = 1 * 1024 * 1024 * 1024;
+ DeviceSize deviceSize;
+ deviceSize.maxSectors = 512;
+ deviceSize.sectorSize = 0x200;
+ deviceSize.sectorsPerCluster = 0x08;
+ deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
+ deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
+ Memory::WriteStruct(pointer, &deviceSize);
+ RETURN(0);
+ } else {
+ RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
+ }
+ return;
+ }
}
- RETURN(0);
- return;
}
@@ -544,7 +637,8 @@ void sceIoDevctl() //(const char *name, int cmd, void *arg, size_t arglen, void
case 1: // EMULATOR_DEVCTL__GET_HAS_DISPLAY
if (Memory::IsValidAddress(outPtr))
Memory::Write_U32(0, outPtr); // TODO: Make a headless mode for running tests!
- break;
+ RETURN(0);
+ return;
case 2: // EMULATOR_DEVCTL__SEND_OUTPUT
{
std::string data(Memory::GetCharPointer(argAddr), argLen);
@@ -561,14 +655,20 @@ void sceIoDevctl() //(const char *name, int cmd, void *arg, size_t arglen, void
{
DEBUG_LOG(HLE, "%s", data.c_str());
}
- break;
+ RETURN(0);
+ return;
}
case 3: // EMULATOR_DEVCTL__IS_EMULATOR
if (Memory::IsValidAddress(outPtr))
Memory::Write_U32(1, outPtr); // TODO: Make a headless mode for running tests!
- break;
+ RETURN(0);
+ return;
}
- retVal = 0;
+
+ ERROR_LOG(HLE, "sceIoDevCtl: UNKNOWN PARAMETERS");
+
+ RETURN(0);
+ return;
}
//089c6d1c weird branch
@@ -579,7 +679,7 @@ void sceIoDevctl() //(const char *name, int cmd, void *arg, size_t arglen, void
089c6c78 ]: HLE: sceIoDevctl("fatms0:", 02415821, 09ffb9c4, 4, 00000000, 0) (z_un_089c6bc4)
089c6cac ]: HLE: sceIoDevctl("mscmhc0:", 02025806, 00000000, 0, 09ffb9c8, 4) (z_un_089c6bc4)
*/
- RETURN(retVal);
+ RETURN(SCE_KERNEL_ERROR_UNSUP);
}
void sceIoRename() //(const char *oldname, const char *newname);
diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp
index 8f1d562617..bd8d92bbec 100644
--- a/Core/HLE/sceKernel.cpp
+++ b/Core/HLE/sceKernel.cpp
@@ -34,7 +34,6 @@
#include "sceIo.h"
#include "sceKernel.h"
#include "sceKernelAlarm.h"
-#include "sceKernelCallback.h"
#include "sceKernelInterrupt.h"
#include "sceKernelThread.h"
#include "sceKernelMemory.h"
@@ -119,8 +118,8 @@ void sceKernelExitGame()
void sceKernelRegisterExitCallback()
{
- u32 cbid = PARAM(0);
- ERROR_LOG(HLE,"UNIMPL sceKernelRegisterExitCallback(%i)", cbid);
+ u32 cbId = PARAM(0);
+ ERROR_LOG(HLE,"UNIMPL sceKernelRegisterExitCallback(%i)", cbId);
RETURN(0);
}
@@ -223,6 +222,7 @@ void KernelObjectPool::Clear()
}
memset(pool, 0, sizeof(KernelObject*)*maxCount);
}
+
KernelObject *&KernelObjectPool::operator [](SceUID handle)
{
_dbg_assert_msg_(HLE, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
@@ -231,7 +231,7 @@ KernelObject *&KernelObjectPool::operator [](SceUID handle)
void KernelObjectPool::List()
{
- for (int i=0; isize = sizeof(Callback);
- strcpy(c->name, name);
-
- c->entrypoint = entrypoint;
- c->threadId = __KernelGetCurThread();
- c->argument = callbackArg;
- c->actionAfter = actionAfter;
-
- return id;
-}
-
-
-//extern Thread *currentThread;
-void sceKernelCreateCallback()
-{
- u32 entrypoint = PARAM(1);
- u32 callbackArg = PARAM(2);
-
- const char *name = Memory::GetCharPointer(PARAM(0));
-
- u32 id = __KernelCreateCallback(name, entrypoint, callbackArg);
-
- DEBUG_LOG(HLE,"%i=sceKernelCreateCallback(name=%s,entry= %08x, callbackArg = %08x)", id, name, entrypoint, callbackArg);
-
- RETURN(id);
-}
-
-void sceKernelDeleteCallback()
-{
- SceUID id = PARAM(0);
- DEBUG_LOG(HLE,"sceKernelDeleteCallback(%i)", id);
- RETURN(kernelObjects.Destroy(id));
-}
-
-void sceKernelNotifyCallback()
-{
- SceUID cbid = PARAM(0);
- u32 arg = PARAM(1);
- DEBUG_LOG(HLE,"sceKernelNotifyCallback(%i, %i)", cbid, arg);
-
- __KernelNotifyCallback(__KernelGetCurThread(), cbid, arg);
- RETURN(0);
-}
-
-void sceKernelCancelCallback()
-{
- SceUID cbid = PARAM(0);
- ERROR_LOG(HLE,"UNIMPL sceKernelCancelCallback(%i)", cbid);
- //__KernelCancelCallback(__KernelGetCurThread(), cbid);
- RETURN(0);
-}
-
-void sceKernelGetCallbackCount()
-{
- SceUID cbid = PARAM(0);
- ERROR_LOG(HLE,"UNIMPL sceKernelGetCallbackCount(%i)", cbid);
-
- RETURN(0);
-}
-
-void sceKernelReferCallbackStatus()
-{
- SceUID cbid = PARAM(0);
- u32 statusAddr = PARAM(1);
- ERROR_LOG(HLE,"UNIMPL sceKernelReferCallbackStatus(%i, %08x)", cbid, statusAddr);
-
- RETURN(0);
-}
-
-void __KernelCallCallback(SceUID id)
-{
- //First, make sure we're on the right thread
- u32 error;
- Callback *c = kernelObjects.Get(id, error);
- if (c)
- {
- if (c->threadId == __KernelGetCurThread())
- {
- //Alright, we're on the right thread
-
- // Save the few regs that need saving
- c->savedPC = currentMIPS->pc;
- c->savedRA = currentMIPS->r[MIPS_REG_RA];
- c->savedV0 = currentMIPS->r[MIPS_REG_V0];
- c->savedV1 = currentMIPS->r[MIPS_REG_V1];
- c->savedIdRegister = currentMIPS->r[MIPS_REG_CB_ID];
-
- // Set up the new state
- // TODO: check?
- CallbackNotification *notify = __KernelGetCallbackNotification(id);
- if (notify != NULL)
- {
- currentMIPS->r[4] = notify->count;
- currentMIPS->r[5] = notify->arg;
- notify->count = 0;
- notify->arg = 0;
- }
- else
- {
- currentMIPS->r[4] = 0;
- currentMIPS->r[5] = 0;
- }
-
- currentMIPS->r[6] = c->argument;
- currentMIPS->pc = c->entrypoint;
- currentMIPS->r[MIPS_REG_RA] = __KernelCallbackReturnAddress();
-
- g_inCbCount++;
- }
- }
- else
- {
- //ARGH!
- }
-}
-
-void _sceKernelReturnFromCallback()
-{
- SceUID cbid = currentMIPS->r[MIPS_REG_CB_ID]; // yeah!
- DEBUG_LOG(HLE,"_sceKernelReturnFromCallback(cbid=%i)", cbid);
- // Value returned by the callback function
- u32 retVal = currentMIPS->r[MIPS_REG_V0];
-
- u32 error;
- Callback *cb = kernelObjects.Get(cbid, error);
- if (!cb)
- {
- ERROR_LOG(HLE, "_sceKernelReturnFromCallback(): INVALID CBID in register! we're screwed");
- return;
- }
-
- currentMIPS->pc = cb->savedPC;
- currentMIPS->r[MIPS_REG_RA] = cb->savedRA;
- currentMIPS->r[MIPS_REG_V0] = cb->savedV0;
- currentMIPS->r[MIPS_REG_V1] = cb->savedV1;
- currentMIPS->r[MIPS_REG_CB_ID] = cb->savedIdRegister;
-
- // Callbacks that don't return 0 are deleted. But should this be done here?
- if (retVal != 0 || cb->forceDelete)
- {
- DEBUG_LOG(HLE, "_sceKernelReturnFromCallback(): Callback returned non-zero, gets deleted!");
- kernelObjects.Destroy(cbid);
- }
-
- if (cb->actionAfter)
- {
- cb->actionAfter->run();
- }
- g_inCbCount--;
- // yeah! back in the real world, let's keep going....
-}
-
-u32 sceKernelCheckCallback()
-{
- //only check those of current thread
- ERROR_LOG(HLE,"UNIMPL sceKernelCheckCallback()");
-
- // HACK that makes the audio thread work in Puyo Puyo Fever - the main thread never yields!
- // Probably because callbacks aren't working right.
- __KernelReSchedule("checkcallbackhack");
-
- return 0;
-}
-
-bool __KernelInCallback()
-{
- return (g_inCbCount != 0);
-}
-
diff --git a/Core/HLE/sceKernelCallback.h b/Core/HLE/sceKernelCallback.h
deleted file mode 100644
index f3af93cbe4..0000000000
--- a/Core/HLE/sceKernelCallback.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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/.
-
-#pragma once
-
-#include "Action.h"
-
-// Internal access - used by sceSetGeCallback
-u32 __KernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg, Action *actionAfter = 0);
-
-void sceKernelCreateCallback();
-void sceKernelDeleteCallback();
-void sceKernelNotifyCallback();
-void sceKernelCancelCallback();
-void sceKernelGetCallbackCount();
-void _sceKernelReturnFromCallback();
-u32 sceKernelCheckCallback();
-void sceKernelGetCallbackCount();
-void sceKernelReferCallbackStatus();
-bool __KernelInCallback();
-
diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp
index 2fd592176d..8262bf1faf 100644
--- a/Core/HLE/sceKernelMbx.cpp
+++ b/Core/HLE/sceKernelMbx.cpp
@@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "sceKernel.h"
+#include "sceKernelThread.h"
#include "sceKernelMbx.h"
#include "HLE.h"
@@ -104,6 +105,7 @@ void sceKernelReceiveMbxCB()
SceUInt uid = PARAM(0);
u32 packetAddrPtr = PARAM(1);
u32 timeoutPtr = PARAM(2);
+ __KernelCheckCallbacks();
ERROR_LOG(HLE, "UNIMPL sceKernelReceiveMbxCB(%i, %08x, %08x)", uid, packetAddrPtr, timeoutPtr);
RETURN(0);
diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp
index b96f716985..3049d35a35 100644
--- a/Core/HLE/sceKernelMemory.cpp
+++ b/Core/HLE/sceKernelMemory.cpp
@@ -228,6 +228,7 @@ void sceKernelAllocateFplCB()
RETURN(0);
} else {
// TODO: Should block and process callbacks!
+ __KernelCheckCallbacks();
RETURN(0);
}
@@ -546,6 +547,7 @@ void sceKernelAllocateVplCB()
else
{
ERROR_LOG(HLE, "sceKernelAllocateVplCB FAILURE");
+ __KernelCheckCallbacks();
RETURN(-1);
}
}
@@ -633,8 +635,8 @@ const HLEFunction SysMemUserForUser[] =
{0x315AD3A0,0,"sceKernelSetCompiledSdkVersion380_390"},
{0xEBD5C3E6,0,"sceKernelSetCompiledSdkVersion395"},
{0xf77d77cb,sceKernelSetCompilerVersion,"sceKernelSetCompilerVersion"},
- {0x35669d4c,0,"SysMemUserForUser_35669d4c"},
- {0x1b4217bc,0,"SysMemUserForUser_1b4217bc"},
+ {0x35669d4c,0,"sceKernelSetCompiledSdkVersion600_602"}, //??
+ {0x1b4217bc,0,"sceKernelSetCompiledSdkVersion603_605"},
};
diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp
index e9dd558cca..ef1c137fae 100644
--- a/Core/HLE/sceKernelModule.cpp
+++ b/Core/HLE/sceKernelModule.cpp
@@ -146,6 +146,17 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
ptr = newptr;
pspDecryptPRX(in, (u8*)ptr, head->psp_size);
}
+
+ if (*(u32*)ptr == 0x4543537e) { // "~SCE"
+ ERROR_LOG(HLE, "Wrong magic number %08x (~SCE, kernel module?)",*(u32*)ptr);
+ *error_string = "Kernel module?";
+ if (newptr)
+ {
+ delete [] newptr;
+ }
+ kernelObjects.Destroy(module->GetUID());
+ return 0;
+ }
if (*(u32*)ptr != 0x464c457f)
{
diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp
index dbd1626076..493572b714 100644
--- a/Core/HLE/sceKernelSemaphore.cpp
+++ b/Core/HLE/sceKernelSemaphore.cpp
@@ -215,6 +215,7 @@ void sceKernelWaitSemaCB()
{
s->waitingThreads.push_back(__KernelGetCurThread());
__KernelWaitCurThread(WAITTYPE_SEMA, id, wantedCount, 0, true);
+ __KernelCheckCallbacks();
return;
}
DEBUG_LOG(HLE,"After: CurrentCount: %i, Signal: %i", s->ns.currentCount, wantedCount);
diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp
index 5512e6c1d6..af21990a06 100644
--- a/Core/HLE/sceKernelThread.cpp
+++ b/Core/HLE/sceKernelThread.cpp
@@ -15,12 +15,17 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+#include
+#include
+
#include "HLE.h"
#include "HLETables.h"
#include "../MIPS/MIPSInt.h"
#include "../MIPS/MIPSCodeUtils.h"
#include "../MIPS/MIPS.h"
#include "../../Core/CoreTiming.h"
+#include "../../Core/MemMap.h"
+#include "../../Common/Action.h"
#include "sceAudio.h"
#include "sceKernel.h"
@@ -28,9 +33,6 @@
#include "sceKernelThread.h"
#include "sceKernelModule.h"
#include "sceKernelInterrupt.h"
-#include "sceKernelCallback.h"
-
-#include
enum ThreadStatus
{
@@ -85,6 +87,7 @@ struct SceKernelSysClock {
u32 hi;
};
+// Real PSP struct, don't change the fields
struct NativeThread
{
u32 nativeSize;
@@ -129,18 +132,21 @@ public:
nt.waitID,
waitValue);
}
- static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; }
- int GetIDType() const { return SCE_KERNEL_TMID_Thread; }
- bool GrabStack(u32 &stackSize)
+
+ static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; }
+
+ int GetIDType() const { return SCE_KERNEL_TMID_Thread; }
+
+ bool AllocateStack(u32 &stackSize)
{
if (nt.attr & PSP_THREAD_ATTR_KERNEL)
{
// Allocate stacks for kernel threads (idle) in kernel RAM
- stackBlock = kernelMemory.Alloc(stackSize, true, "stack");
+ stackBlock = kernelMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
}
else
{
- stackBlock = userMemory.Alloc(stackSize, true, "stack");
+ stackBlock = userMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
}
if (stackBlock == (u32)-1)
{
@@ -157,9 +163,14 @@ public:
context.r[MIPS_REG_SP] -= 512;
return true;
}
+
~Thread()
{
- userMemory.Free(stackBlock);
+ if (nt.attr & PSP_THREAD_ATTR_KERNEL) {
+ kernelMemory.Free(stackBlock);
+ } else {
+ userMemory.Free(stackBlock);
+ }
}
NativeThread nt;
@@ -171,11 +182,64 @@ public:
ThreadContext context;
- std::vector callbacks;
+ std::set registeredCallbacks[THREAD_CALLBACK_NUM_TYPES];
+ std::list readyCallbacks[THREAD_CALLBACK_NUM_TYPES];
u32 stackBlock;
};
+struct NativeCallback
+{
+ SceUInt size;
+ char name[32];
+ SceUID threadId;
+ u32 entrypoint;
+ u32 commonArgument;
+
+ int notifyCount;
+ int notifyArg;
+};
+
+class Callback : public KernelObject
+{
+public:
+ const char *GetName() {return nc.name;}
+ const char *GetTypeName() {return "CallBack";}
+
+ void GetQuickInfo(char *ptr, int size)
+ {
+ sprintf(ptr, "thread=%i, argument= %08x",
+ //hackAddress,
+ nc.threadId,
+ nc.commonArgument);
+ }
+
+ ~Callback()
+ {
+ }
+
+ static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_CBID; }
+ int GetIDType() const { return SCE_KERNEL_TMID_Callback; }
+
+ NativeCallback nc;
+
+ u32 savedPC;
+ u32 savedRA;
+ u32 savedV0;
+ u32 savedV1;
+ u32 savedIdRegister;
+
+ /*
+ SceUInt attr;
+ SceUInt initPattern;
+ SceUInt currentPattern;
+ int numWaitThreads;
+ */
+
+ bool forceDelete;
+};
+
+int g_inCbCount = 0;
Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr);
@@ -200,6 +264,12 @@ SceUID curModule;
//STATE END
//////////////////////////////////////////////////////////////////////////
+// TODO: Should move to this wrapper so we can keep the current thread as a SceUID instead
+// of a dangerous raw pointer.
+Thread *__GetCurrentThread() {
+ return currentThread;
+}
+
u32 __KernelCallbackReturnAddress()
{
return cbReturnHackAddr;
@@ -240,9 +310,9 @@ void __KernelThreadingInit()
eventScheduledWakeup = CoreTiming::RegisterEvent("ScheduledWakeup", &hleScheduledWakeup);
// Create the two idle threads, as well. With the absolute minimal possible priority.
- // Zero stack size. Hm, if callbacks are ever to run on these threads, that's not a good idea.
- __KernelCreateThread(threadIdleID[0], 0, "idle0", idleThreadHackAddr, 0x7f, 0, PSP_THREAD_ATTR_KERNEL);
- __KernelCreateThread(threadIdleID[1], 0, "idle1", idleThreadHackAddr, 0x7f, 0, PSP_THREAD_ATTR_KERNEL);
+ // 4096 stack size - don't know what the right value is. Hm, if callbacks are ever to run on these threads...
+ __KernelCreateThread(threadIdleID[0], 0, "idle0", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL);
+ __KernelCreateThread(threadIdleID[1], 0, "idle1", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL);
// These idle threads are later started in LoadExec, which calls __KernelStartIdleThreads below.
}
@@ -296,44 +366,6 @@ u32 __KernelGetWaitValue(SceUID threadID, u32 &error)
}
}
-// TODO: If cbid == -1, notify the callback ID on all threads that have it.
-void __KernelNotifyCallback(SceUID threadID, SceUID cbid, u32 notifyArg)
-{
- u32 error;
- Thread *t = kernelObjects.Get(threadID, error);
- if (t)
- {
- for (size_t i = 0; i < t->callbacks.size(); t++)
- {
- if (t->callbacks[i].cbid == cbid)
- {
- t->callbacks[i].arg = notifyArg;
- t->callbacks[i].count++;
- return;
- }
- }
- CallbackNotification cb;
- cb.cbid = cbid;
- cb.arg = notifyArg;
- cb.count = 1;
- }
- // TODO: error checking
-}
-
-CallbackNotification *__KernelGetCallbackNotification(SceUID cbid)
-{
- u32 error;
- Thread *t = kernelObjects.Get(__KernelGetCurThread(), error);
- for (size_t i = 0; i < t->callbacks.size(); t++)
- {
- if (t->callbacks[i].cbid == cbid)
- {
- return &t->callbacks[i];
- }
- }
- return 0;
-}
-
void sceKernelReferThreadStatus()
{
SceUID threadID = PARAM(0);
@@ -472,6 +504,8 @@ bool __KernelTriggerWait(WaitType type, int id, bool dontSwitch)
{
t->nt.status = THREADSTATUS_READY;
}
+ // Non-waiting threads do not process callbacks.
+ t->isProcessingCallbacks = false;
doneAnything = true;
}
}
@@ -548,7 +582,7 @@ void __KernelRemoveFromThreadQueue(Thread *t)
{
if (threadqueue[i] == t)
{
- DEBUG_LOG(HLE, "Deleted thread %p from thread queue", t);
+ DEBUG_LOG(HLE, "Deleted thread %p (%i) from thread queue", t, t->GetUID());
threadqueue.erase(threadqueue.begin() + i);
return;
}
@@ -705,7 +739,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32
strncpy(t->nt.name, name, 32);
t->context.r[MIPS_REG_RA] = threadReturnHackAddr; //hack! TODO fix
- t->GrabStack(t->nt.stackSize); // can change the stacksize!
+ t->AllocateStack(t->nt.stackSize); // can change the stacksize!
return t;
}
@@ -792,6 +826,7 @@ u32 sceKernelStartThread()
//now copy argument to stack
for (int i = 0; i < (int)argSize; i++)
Memory::Write_U8(Memory::Read_U8(argBlockPtr + i), sp + i);
+
return 0;
}
else
@@ -833,6 +868,8 @@ void _sceKernelReturnFromThread()
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
__KernelReSchedule("return from thread");
+
+ // The stack will be deallocated when the thread is deleted.
}
void sceKernelExitThread()
@@ -844,6 +881,8 @@ void sceKernelExitThread()
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
__KernelReSchedule("exited thread");
+
+ // The stack will be deallocated when the thread is deleted.
}
void _sceKernelExitThread()
@@ -855,6 +894,8 @@ void _sceKernelExitThread()
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
__KernelReSchedule("exit-deleted thread");
+
+ // The stack will be deallocated when the thread is deleted.
}
void sceKernelExitDeleteThread()
@@ -867,6 +908,8 @@ void sceKernelExitDeleteThread()
ERROR_LOG(HLE,"sceKernelExitDeleteThread()");
currentThread->nt.status = THREADSTATUS_DORMANT;
currentThread->nt.exitStatus = PARAM(0);
+ //userMemory.Free(currentThread->stackBlock);
+ currentThread->stackBlock = -1;
__KernelRemoveFromThreadQueue(t);
currentThread = 0;
@@ -989,6 +1032,7 @@ void sceKernelDelayThreadCB()
SceUID curThread = __KernelGetCurThread();
__KernelScheduleWakeup(curThread, usec);
__KernelWaitCurThread(WAITTYPE_DELAY, curThread, 0, 0, true);
+ __KernelCheckCallbacks();
}
void sceKernelDelayThread()
@@ -1039,9 +1083,11 @@ void sceKernelSleepThreadCB()
DEBUG_LOG(HLE,"sceKernelSleepThreadCB()");
//set it to waiting
currentThread->nt.wakeupCount--;
- DEBUG_LOG(HLE,"sceKernelSleepThread() - wakeupCount decremented to %i", currentThread->nt.wakeupCount);
- if (currentThread->nt.wakeupCount < 0)
+ DEBUG_LOG(HLE,"sceKernelSleepThreadCB() - wakeupCount decremented to %i", currentThread->nt.wakeupCount);
+ if (currentThread->nt.wakeupCount < 0) {
__KernelWaitCurThread(WAITTYPE_SLEEP, 0, 0, 0, true);
+ __KernelCheckCallbacks();
+ }
else
{
RETURN(0);
@@ -1056,12 +1102,11 @@ void sceKernelWaitThreadEnd()
Thread *t = kernelObjects.Get(id, error);
if (t)
{
- if (t->nt.status != THREADSTATUS_DORMANT)
- {
+ if (t->nt.status != THREADSTATUS_DORMANT) {
__KernelWaitCurThread(WAITTYPE_THREADEND, id, 0, 0, false);
- return;
+ } else {
+ DEBUG_LOG(HLE,"sceKernelWaitThreadEnd - thread %i already ended. Doing nothing.", id);
}
- DEBUG_LOG(HLE,"sceKernelWaitThreadEnd - thread %i already ended. Doing nothing.", id);
}
else
{
@@ -1078,12 +1123,12 @@ void sceKernelWaitThreadEndCB()
Thread *t = kernelObjects.Get(id, error);
if (t)
{
- if (t->nt.status != THREADSTATUS_DORMANT)
- {
+ if (t->nt.status != THREADSTATUS_DORMANT) {
__KernelWaitCurThread(WAITTYPE_THREADEND, id, 0, 0, true);
- return;
- }
- DEBUG_LOG(HLE,"sceKernelWaitThreadEnd - thread %i already ended. Doing nothing.", id);
+ } else {
+ DEBUG_LOG(HLE,"sceKernelWaitThreadEnd - thread %i already ended. Doing nothing.", id);
+ }
+ __KernelCheckCallbacks();
}
else
{
@@ -1103,3 +1148,303 @@ void sceKernelResumeThread()
DEBUG_LOG(HLE,"UNIMPL sceKernelResumeThread");
RETURN(0);
}
+
+//////////////////////////////////////////////////////////////////////////
+// CALLBACKS
+//////////////////////////////////////////////////////////////////////////
+
+
+// Internal API
+u32 __KernelCreateCallback(const char *name, u32 entrypoint, u32 commonArg)
+{
+ Callback *cb = new Callback;
+ SceUID id = kernelObjects.Create(cb);
+
+ cb->nc.size = sizeof(NativeCallback);
+ strcpy(cb->nc.name, name);
+
+ cb->nc.entrypoint = entrypoint;
+ cb->nc.threadId = __KernelGetCurThread();
+ cb->nc.commonArgument = commonArg;
+ cb->nc.notifyCount = 0;
+ cb->nc.notifyArg = 0;
+
+ cb->forceDelete = false;
+
+ return id;
+}
+
+void sceKernelCreateCallback()
+{
+ u32 entrypoint = PARAM(1);
+ u32 callbackArg = PARAM(2);
+
+ const char *name = Memory::GetCharPointer(PARAM(0));
+
+ u32 id = __KernelCreateCallback(name, entrypoint, callbackArg);
+
+ DEBUG_LOG(HLE,"%i=sceKernelCreateCallback(name=%s,entry= %08x, callbackArg = %08x)", id, name, entrypoint, callbackArg);
+
+ RETURN(id);
+}
+
+void sceKernelDeleteCallback()
+{
+ SceUID id = PARAM(0);
+ DEBUG_LOG(HLE,"sceKernelDeleteCallback(%i)", id);
+
+ // TODO: Make sure it's gone from all threads first!
+
+ RETURN(kernelObjects.Destroy(id));
+}
+
+// Rarely used
+void sceKernelNotifyCallback()
+{
+ SceUID cbId = PARAM(0);
+ u32 arg = PARAM(1);
+ DEBUG_LOG(HLE,"sceKernelNotifyCallback(%i, %i) UNIMPL", cbId, arg);
+
+ // __KernelNotifyCallback(__KernelGetCurThread(), cbId, arg);
+ RETURN(0);
+}
+
+void sceKernelCancelCallback()
+{
+ SceUID cbId = PARAM(0);
+ ERROR_LOG(HLE,"sceKernelCancelCallback(%i) - BAD", cbId);
+ u32 error;
+ Callback *cb = kernelObjects.Get(cbId, error);
+ if (cb) {
+ // This is what JPCSP does. Huh?
+ cb->nc.notifyArg = 0;
+ RETURN(0);
+ } else {
+ ERROR_LOG(HLE,"sceKernelCancelCallback(%i) - bad cbId", cbId);
+ RETURN(error);
+ }
+ RETURN(0);
+}
+
+void sceKernelGetCallbackCount()
+{
+ SceUID cbId = PARAM(0);
+ u32 error;
+ Callback *cb = kernelObjects.Get(cbId, error);
+ if (cb) {
+ RETURN(cb->nc.notifyCount);
+ } else {
+ ERROR_LOG(HLE,"sceKernelGetCallbackCount(%i) - bad cbId", cbId);
+ RETURN(error);
+ }
+}
+
+void sceKernelReferCallbackStatus()
+{
+ SceUID cbId = PARAM(0);
+ u32 statusAddr = PARAM(1);
+ u32 error;
+ Callback *c = kernelObjects.Get(cbId, error);
+ if (c) {
+ DEBUG_LOG(HLE,"sceKernelReferCallbackStatus(%i, %08x)", cbId, statusAddr);
+ if (Memory::IsValidAddress(statusAddr)) {
+ Memory::WriteStruct(statusAddr, &c->nc);
+ } // else TODO
+ RETURN(0);
+ } else {
+ ERROR_LOG(HLE,"sceKernelReferCallbackStatus(%i, %08x) - bad cbId", cbId, statusAddr);
+ RETURN(error);
+ }
+}
+
+
+// Context switches to the thread and executes the callback.
+u32 __KernelRunCallbackOnThread(SceUID cbId, Thread *thread)
+{
+ if (g_inCbCount > 0) {
+ WARN_LOG(HLE, "__KernelRunCallbackOnThread: Already in a callback!");
+ }
+
+ u32 error;
+ Callback *cb = kernelObjects.Get(cbId, error);
+ if (!cb) {
+ return error;
+ }
+
+ // First, context switch to the thread.
+ Thread *curThread = __GetCurrentThread();
+ if (thread != curThread) {
+ __KernelSaveContext(&curThread->context);
+ __KernelLoadContext(&thread->context);
+ }
+
+ //Alright, we're on the right thread
+
+ // Save the few regs that need saving
+ cb->savedPC = currentMIPS->pc;
+ cb->savedRA = currentMIPS->r[MIPS_REG_RA];
+ cb->savedV0 = currentMIPS->r[MIPS_REG_V0];
+ cb->savedV1 = currentMIPS->r[MIPS_REG_V1];
+ cb->savedIdRegister = currentMIPS->r[MIPS_REG_CB_ID];
+
+ // Set up the new state
+ // TODO: check?
+ currentMIPS->r[MIPS_REG_A0] = cb->nc.notifyCount;
+ currentMIPS->r[MIPS_REG_A1] = cb->nc.notifyArg;
+ currentMIPS->r[MIPS_REG_A2] = cb->nc.commonArgument;
+ currentMIPS->pc = cb->nc.entrypoint;
+ currentMIPS->r[MIPS_REG_RA] = __KernelCallbackReturnAddress();
+ currentMIPS->r[MIPS_REG_CB_ID] = cbId;
+
+ // Clear the notify count / arg
+ cb->nc.notifyCount = 0;
+ cb->nc.notifyArg = 0;
+
+ g_inCbCount++;
+ return 0;
+}
+
+void _sceKernelReturnFromCallback()
+{
+ SceUID cbId = currentMIPS->r[MIPS_REG_CB_ID];
+ // Value returned by the callback function
+ u32 retVal = currentMIPS->r[MIPS_REG_V0];
+ DEBUG_LOG(HLE,"_sceKernelReturnFromCallback(cbId=%i), returned %08x", cbId, retVal);
+
+ u32 error;
+ Callback *cb = kernelObjects.Get(cbId, error);
+ if (!cb)
+ {
+ ERROR_LOG(HLE, "_sceKernelReturnFromCallback(): INVALID CBID %i in register! we're screwed", cbId);
+ return;
+ }
+
+ currentMIPS->pc = cb->savedPC;
+ currentMIPS->r[MIPS_REG_RA] = cb->savedRA;
+ currentMIPS->r[MIPS_REG_V0] = cb->savedV0;
+ currentMIPS->r[MIPS_REG_V1] = cb->savedV1;
+ currentMIPS->r[MIPS_REG_CB_ID] = cb->savedIdRegister;
+
+ // Callbacks that don't return 0 are deleted. But should this be done here?
+ if (retVal != 0 || cb->forceDelete)
+ {
+ DEBUG_LOG(HLE, "_sceKernelReturnFromCallback(): Callback returned non-zero, gets deleted!");
+ kernelObjects.Destroy(cbId);
+ }
+
+ g_inCbCount--;
+
+ // yeah! back in the real world, let's keep going. Should we process more callbacks?
+}
+
+// Check callbacks on the current thread only.
+// Returns true if any callbacks were processed on the current thread.
+bool __KernelCheckThreadCallbacks(Thread *thread) {
+ if (!thread->isProcessingCallbacks)
+ return false;
+
+ for (int i = 0; i < THREAD_CALLBACK_NUM_TYPES; i++) {
+ if (thread->readyCallbacks[i].size()) {
+ SceUID readyCallback = thread->readyCallbacks[i].front();
+ thread->readyCallbacks[i].pop_front();
+ __KernelRunCallbackOnThread(readyCallback, thread);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Checks for callbacks on all threads
+bool __KernelCheckCallbacks() {
+ SceUID currentThread = __KernelGetCurThread();
+ // do {
+ bool processed = false;
+
+ for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) {
+ Thread *thread = *iter;
+ if (thread->isProcessingCallbacks && __KernelCheckThreadCallbacks(thread)) {
+ processed = true;
+ }
+ }
+ // } while (processed && currentThread == __KernelGetCurThread());
+ return processed;
+}
+
+u32 sceKernelCheckCallback()
+{
+ Thread *curThread = __GetCurrentThread();
+
+ // This thread can now process callbacks.
+ curThread->isProcessingCallbacks = true;
+
+ int callbacksProcessed = __KernelCheckThreadCallbacks(curThread) ? 1 : 0;
+
+ // Note - same thread as above - checking callbacks may switch threads.
+ curThread->isProcessingCallbacks = false;
+
+ if (callbacksProcessed) {
+ ERROR_LOG(HLE,"sceKernelCheckCallback() - processed a callback.");
+ } else {
+ DEBUG_LOG(HLE,"sceKernelCheckCallback() - no callbacks to process, doing nothing");
+ }
+
+ return callbacksProcessed;
+}
+
+bool __KernelInCallback()
+{
+ return (g_inCbCount != 0);
+}
+
+
+u32 __KernelRegisterCallback(RegisteredCallbackType type, SceUID cbId)
+{
+ Thread *t = __GetCurrentThread();
+ if (t->registeredCallbacks[type].find(cbId) == t->registeredCallbacks[type].end()) {
+ t->registeredCallbacks[type].insert(cbId);
+ return 0;
+ } else {
+ return SCE_KERNEL_ERROR_INVAL;
+ }
+}
+
+u32 __KernelUnregisterCallback(RegisteredCallbackType type, SceUID cbId)
+{
+ Thread *t = __GetCurrentThread();
+ if (t->registeredCallbacks[type].find(cbId) != t->registeredCallbacks[type].end()) {
+ t->registeredCallbacks[type].erase(cbId);
+ return 0;
+ } else {
+ return SCE_KERNEL_ERROR_INVAL;
+ }
+}
+
+void __KernelNotifyCallback(RegisteredCallbackType type, SceUID threadId, SceUID cbId, int notifyArg)
+{
+ u32 error;
+
+ Callback *cb = kernelObjects.Get(cbId, error);
+ cb->nc.notifyCount++;
+ cb->nc.notifyArg = notifyArg;
+
+ Thread *t = kernelObjects.Get(threadId, error);
+ t->readyCallbacks[type].remove(cbId);
+ t->readyCallbacks[type].push_back(cbId);
+}
+
+// TODO: If cbId == -1, notify the callback ID on all threads that have it.
+u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int notifyArg)
+{
+ for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) {
+ Thread *t = *iter;
+ for (std::set::iterator citer = t->registeredCallbacks[type].begin(); citer != t->registeredCallbacks[type].end(); citer++) {
+ if (cbId == -1 || cbId == *citer) {
+ __KernelNotifyCallback(type, t->GetUID(), *citer, notifyArg);
+ }
+ }
+ }
+
+ // checkCallbacks on other threads?
+ return 0;
+}
+
diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h
index fc64926761..9bca5a566e 100644
--- a/Core/HLE/sceKernelThread.h
+++ b/Core/HLE/sceKernelThread.h
@@ -85,14 +85,6 @@ struct ThreadContext
u32 fcr31;
};
-struct CallbackNotification
-{
- SceUID cbid;
- int count;
- int arg;
-};
-
-
// Internal API, used by implementations of kernel functions
void __KernelThreadingInit();
@@ -111,10 +103,30 @@ u32 __KernelResumeThread(SceUID threadID); // can return an error value
u32 __KernelGetWaitValue(SceUID threadID, u32 &error);
void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, int timeout, bool processCallbacks);
void __KernelReSchedule(const char *reason = "no reason");
-void __KernelNotifyCallback(SceUID threadID, SceUID cbid, u32 arg);
-CallbackNotification *__KernelGetCallbackNotification(SceUID cbid);
+
+// Registered callback types
+enum RegisteredCallbackType {
+ THREAD_CALLBACK_UMD = 0,
+ THREAD_CALLBACK_IO = 1,
+ THREAD_CALLBACK_MEMORYSTICK = 2,
+ THREAD_CALLBACK_MEMORYSTICK_FAT = 3,
+ THREAD_CALLBACK_POWER = 4,
+ THREAD_CALLBACK_EXIT = 5,
+ THREAD_CALLBACK_USER_DEFINED = 6,
+ THREAD_CALLBACK_SIZE = 7,
+ THREAD_CALLBACK_NUM_TYPES = 8,
+};
+
+// These operate on the current thread
+u32 __KernelRegisterCallback(RegisteredCallbackType type, SceUID cbId);
+u32 __KernelUnregisterCallback(RegisteredCallbackType type, SceUID cbId);
+
+// If cbId == -1, all the callbacks of the type on all the threads get notified.
+// If not, only this particular callback gets notified.
+u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int notifyArg);
+
SceUID __KernelGetCurThread();
void __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution
void __KernelStartIdleThreads();
@@ -124,3 +136,20 @@ void _sceKernelIdle();
u32 __KernelCallbackReturnAddress();
u32 __KernelInterruptReturnAddress();
+
+// Internal access - used by sceSetGeCallback
+u32 __KernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg);
+
+void sceKernelCreateCallback();
+void sceKernelDeleteCallback();
+void sceKernelNotifyCallback();
+void sceKernelCancelCallback();
+void sceKernelGetCallbackCount();
+void _sceKernelReturnFromCallback();
+u32 sceKernelCheckCallback();
+void sceKernelGetCallbackCount();
+void sceKernelReferCallbackStatus();
+bool __KernelInCallback();
+
+// Should be called by (nearly) all ...CB functions.
+bool __KernelCheckCallbacks();
diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp
index 98baf93980..48ec684729 100644
--- a/Core/MIPS/MIPS.cpp
+++ b/Core/MIPS/MIPS.cpp
@@ -77,6 +77,7 @@ void MIPSState::Reset()
SetWriteMask(b);
pc = 0;
+ prevPC = 0;
hi = 0;
lo = 0;
fpcond = 0;
@@ -155,7 +156,7 @@ void MIPSState::RunLoopUntil(u64 globalTicks)
break;
}
#endif
- // u32 lastpc = pc;
+ prevPC = pc;
if (inDelaySlot)
{
MIPSInterpret(op);
diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h
index 88a297ea93..522971f996 100644
--- a/Core/MIPS/MIPS.h
+++ b/Core/MIPS/MIPS.h
@@ -112,6 +112,7 @@ public:
u32 pc;
u32 nextPC;
+ u32 prevPC;
u32 hi;
u32 lo;
diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp
index f9d02fc58d..f666f7e776 100644
--- a/Core/MIPS/MIPSInt.cpp
+++ b/Core/MIPS/MIPSInt.cpp
@@ -176,10 +176,14 @@ namespace MIPSInt
switch ((op>>16) & 0x1F)
{
- case 0: if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
+ case 0: if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
case 1: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
case 2: if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
case 3: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
+ case 16: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
+ case 17: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
+ case 18: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
+ case 19: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
default:
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
break;
diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp
index 7904f94385..e348b73376 100644
--- a/Core/MIPS/MIPSTables.cpp
+++ b/Core/MIPS/MIPSTables.cpp
@@ -344,10 +344,10 @@ const MIPSInstruction tableRegImm[32] =
INSTR("tnei", &Jit::Comp_Generic, Dis_Generic, 0, 0),
{-2},
- INSTR("bltzal", &Jit::Comp_Generic, Dis_RelBranch, 0, IS_CONDBRANCH|IN_RS|OUT_RA),
- INSTR("bgezal", &Jit::Comp_Generic, Dis_RelBranch, 0, IS_CONDBRANCH|IN_RS|OUT_RA),
- INSTR("bltzall", &Jit::Comp_Generic, Dis_RelBranch, 0, IS_CONDBRANCH|IN_RS|OUT_RA), //L = likely
- INSTR("bgezall", &Jit::Comp_Generic, Dis_RelBranch, 0, IS_CONDBRANCH|IN_RS|OUT_RA),
+ INSTR("bltzal", &Jit::Comp_Generic, Dis_RelBranch, Int_RelBranchRI, IS_CONDBRANCH|IN_RS|OUT_RA),
+ INSTR("bgezal", &Jit::Comp_Generic, Dis_RelBranch, Int_RelBranchRI, IS_CONDBRANCH|IN_RS|OUT_RA),
+ INSTR("bltzall", &Jit::Comp_Generic, Dis_RelBranch, Int_RelBranchRI, IS_CONDBRANCH|IN_RS|OUT_RA), //L = likely
+ INSTR("bgezall", &Jit::Comp_Generic, Dis_RelBranch, Int_RelBranchRI, IS_CONDBRANCH|IN_RS|OUT_RA),
{-2},
{-2},
{-2},
diff --git a/Core/MemMap.h b/Core/MemMap.h
index a7d94d9228..6afd44e118 100644
--- a/Core/MemMap.h
+++ b/Core/MemMap.h
@@ -211,12 +211,15 @@ void Memcpy(const u32 _Address, const void *_Data, const u32 _iLength);
template
void ReadStruct(u32 address, T *ptr)
{
- memcpy(ptr, GetPointer(address), sizeof(*ptr));
+ size_t sz = sizeof(*ptr);
+ memcpy(ptr, GetPointer(address), sz);
}
+
template
void WriteStruct(u32 address, T *ptr)
{
- memcpy(GetPointer(address), ptr, sizeof(*ptr));
+ size_t sz = sizeof(*ptr);
+ memcpy(GetPointer(address), ptr, sz);
}
const char *GetAddressName(u32 address);
diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp
index ad6eea90f3..35e511988a 100644
--- a/Core/MemMapFunctions.cpp
+++ b/Core/MemMapFunctions.cpp
@@ -82,7 +82,7 @@ inline void ReadFromHardware(T &var, const u32 address)
}
else
{
- WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
+ WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x PPC %08x LR %08x", address, currentMIPS->pc, currentMIPS->prevPC, currentMIPS->r[MIPS_REG_RA]);
if (!g_Config.bIgnoreBadMemAccess) {
// TODO: Not sure what the best way to crash is...
exit(0);
diff --git a/Core/Util/BlockAllocator.cpp b/Core/Util/BlockAllocator.cpp
index 77d7e3a612..3f9953ea8b 100644
--- a/Core/Util/BlockAllocator.cpp
+++ b/Core/Util/BlockAllocator.cpp
@@ -30,7 +30,7 @@ void BlockAllocator::Shutdown()
u32 BlockAllocator::Alloc(u32 &size, bool fromTop, const char *tag)
{
// Sanity check
- if (size > rangeSize_) {
+ if (size == 0 || size > rangeSize_) {
ERROR_LOG(HLE, "Clearly bogus size: %08x - failing allocation", size);
return 0;
}
diff --git a/native b/native
index 8288c307cc..652045e462 160000
--- a/native
+++ b/native
@@ -1 +1 @@
-Subproject commit 8288c307cc896b40f9ad000db8f6b00c2b76ad0f
+Subproject commit 652045e46252c51f02196dcd6f769969fb8b3659