mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-16 13:36:59 +00:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
28
src/Cafe/OS/libs/TCL/TCL.cpp
Normal file
28
src/Cafe/OS/libs/TCL/TCL.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/TCL/TCL.h"
|
||||
|
||||
namespace TCL
|
||||
{
|
||||
|
||||
enum class TCL_SUBMISSION_FLAG : uint32
|
||||
{
|
||||
SURFACE_SYNC = 0x400000, // submit surface sync packet before cmd
|
||||
TRIGGER_INTERRUPT = 0x200000, // probably
|
||||
UKN_20000000 = 0x20000000,
|
||||
};
|
||||
|
||||
int TCLSubmitToRing(uint32be* cmd, uint32 cmdLen, uint32be* controlFlags, uint64* submissionTimestamp)
|
||||
{
|
||||
// todo - figure out all the bits of *controlFlags
|
||||
// if submissionTimestamp != nullptr then set it to the timestamp of the submission. Note: We should make sure that uint64's are written atomically by the GPU command processor
|
||||
|
||||
cemu_assert_debug(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
cafeExportRegister("TCL", TCLSubmitToRing, LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
4
src/Cafe/OS/libs/TCL/TCL.h
Normal file
4
src/Cafe/OS/libs/TCL/TCL.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
namespace TCL
|
||||
{
|
||||
void Initialize();
|
||||
}
|
||||
40
src/Cafe/OS/libs/avm/avm.cpp
Normal file
40
src/Cafe/OS/libs/avm/avm.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "avm.h"
|
||||
|
||||
namespace avm
|
||||
{
|
||||
bool AVMIsHDCPAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AVMIsHDCPOn()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AVMGetAnalogContentsProtectionEnable(uint32be* isEnable)
|
||||
{
|
||||
*isEnable = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AVMIsAnalogContentsProtectionOn()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AVMSetAnalogContentsProtectionEnable(sint32 newState)
|
||||
{
|
||||
return true; // returns 1 (true) if new state was applied successfully?
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
cafeExportRegister("avm", AVMIsHDCPAvailable, LogType::Placeholder);
|
||||
cafeExportRegister("avm", AVMIsHDCPOn, LogType::Placeholder);
|
||||
cafeExportRegister("avm", AVMGetAnalogContentsProtectionEnable, LogType::Placeholder);
|
||||
cafeExportRegister("avm", AVMIsAnalogContentsProtectionOn, LogType::Placeholder);
|
||||
cafeExportRegister("avm", AVMSetAnalogContentsProtectionEnable, LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
5
src/Cafe/OS/libs/avm/avm.h
Normal file
5
src/Cafe/OS/libs/avm/avm.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
namespace avm
|
||||
{
|
||||
void Initialize();
|
||||
}
|
||||
257
src/Cafe/OS/libs/camera/camera.cpp
Normal file
257
src/Cafe/OS/libs/camera/camera.cpp
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
#include "Common/precompiled.h"
|
||||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "camera.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/HW/Espresso/PPCCallback.h"
|
||||
|
||||
namespace camera
|
||||
{
|
||||
|
||||
struct CAMInitInfo_t
|
||||
{
|
||||
/* +0x00 */ uint32be ukn00;
|
||||
/* +0x04 */ uint32be width;
|
||||
/* +0x08 */ uint32be height;
|
||||
|
||||
/* +0x0C */ uint32be workMemorySize;
|
||||
/* +0x10 */ MEMPTR<void> workMemory;
|
||||
|
||||
/* +0x14 */ uint32be handlerFuncPtr;
|
||||
|
||||
/* +0x18 */ uint32be ukn18;
|
||||
/* +0x1C */ uint32be fps;
|
||||
|
||||
/* +0x20 */ uint32be ukn20;
|
||||
};
|
||||
|
||||
struct CAMTargetSurface
|
||||
{
|
||||
/* +0x00 */ uint32be surfaceSize;
|
||||
/* +0x04 */ MEMPTR<void> surfacePtr;
|
||||
/* +0x08 */ uint32be ukn08;
|
||||
/* +0x0C */ uint32be ukn0C;
|
||||
/* +0x10 */ uint32be ukn10;
|
||||
/* +0x14 */ uint32be ukn14;
|
||||
/* +0x18 */ uint32be ukn18;
|
||||
/* +0x1C */ uint32be ukn1C;
|
||||
};
|
||||
|
||||
struct CAMCallbackParam
|
||||
{
|
||||
// type 0 - frame decoded | field1 - imagePtr, field2 - imageSize, field3 - ukn (0)
|
||||
// type 1 - ???
|
||||
|
||||
|
||||
/* +0x0 */ uint32be type; // 0 -> Frame decoded
|
||||
/* +0x4 */ uint32be field1;
|
||||
/* +0x8 */ uint32be field2;
|
||||
/* +0xC */ uint32be field3;
|
||||
};
|
||||
|
||||
|
||||
#define CAM_ERROR_SUCCESS 0
|
||||
#define CAM_ERROR_INVALID_HANDLE -8
|
||||
|
||||
std::vector<struct CameraInstance*> g_table_cameraHandles;
|
||||
std::vector<struct CameraInstance*> g_activeCameraInstances;
|
||||
std::recursive_mutex g_mutex_camera;
|
||||
std::atomic_int g_cameraCounter{ 0 };
|
||||
SysAllocator<coreinit::OSAlarm_t, 1> g_alarm_camera;
|
||||
SysAllocator<CAMCallbackParam, 1> g_cameraHandlerParam;
|
||||
|
||||
CameraInstance* GetCameraInstanceByHandle(sint32 camHandle)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
|
||||
if (camHandle <= 0)
|
||||
return nullptr;
|
||||
camHandle -= 1;
|
||||
if (camHandle >= g_table_cameraHandles.size())
|
||||
return nullptr;
|
||||
return g_table_cameraHandles[camHandle];
|
||||
}
|
||||
|
||||
struct CameraInstance
|
||||
{
|
||||
CameraInstance(uint32 frameWidth, uint32 frameHeight, MPTR handlerFunc) : width(frameWidth), height(frameHeight), handlerFunc(handlerFunc) { AcquireHandle(); };
|
||||
~CameraInstance() { if (isOpen) { CloseCam(); } ReleaseHandle(); };
|
||||
|
||||
sint32 handle{ 0 };
|
||||
uint32 width;
|
||||
uint32 height;
|
||||
bool isOpen{false};
|
||||
std::queue<CAMTargetSurface> queue_targetSurfaces;
|
||||
MPTR handlerFunc;
|
||||
|
||||
bool OpenCam()
|
||||
{
|
||||
if (isOpen)
|
||||
return false;
|
||||
isOpen = true;
|
||||
g_activeCameraInstances.push_back(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CloseCam()
|
||||
{
|
||||
if (!isOpen)
|
||||
return false;
|
||||
isOpen = false;
|
||||
vectorRemoveByValue(g_activeCameraInstances, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void QueueTargetSurface(CAMTargetSurface* targetSurface)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
|
||||
cemu_assert_debug(queue_targetSurfaces.size() < 100); // check for sane queue length
|
||||
queue_targetSurfaces.push(*targetSurface);
|
||||
}
|
||||
|
||||
private:
|
||||
void AcquireHandle()
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
|
||||
for (uint32 i = 0; i < g_table_cameraHandles.size(); i++)
|
||||
{
|
||||
if (g_table_cameraHandles[i] == nullptr)
|
||||
{
|
||||
g_table_cameraHandles[i] = this;
|
||||
this->handle = i + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->handle = (sint32)(g_table_cameraHandles.size() + 1);
|
||||
g_table_cameraHandles.push_back(this);
|
||||
}
|
||||
|
||||
void ReleaseHandle()
|
||||
{
|
||||
for (uint32 i = 0; i < g_table_cameraHandles.size(); i++)
|
||||
{
|
||||
if (g_table_cameraHandles[i] == this)
|
||||
{
|
||||
g_table_cameraHandles[i] = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
};
|
||||
|
||||
sint32 CAMGetMemReq(void* ukn)
|
||||
{
|
||||
return 1 * 1024; // always return 1KB
|
||||
}
|
||||
|
||||
sint32 CAMCheckMemSegmentation(void* base, uint32 size)
|
||||
{
|
||||
return CAM_ERROR_SUCCESS; // always return success
|
||||
}
|
||||
|
||||
void ppcCAMUpdate60(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// update all open camera instances
|
||||
size_t numCamInstances = g_activeCameraInstances.size();
|
||||
//for (auto& itr : g_activeCameraInstances)
|
||||
for(size_t i=0; i<numCamInstances; i++)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
|
||||
if (i >= g_activeCameraInstances.size())
|
||||
break;
|
||||
CameraInstance* camInstance = g_activeCameraInstances[i];
|
||||
// todo - handle 30 / 60 FPS
|
||||
if (camInstance->queue_targetSurfaces.empty())
|
||||
continue;
|
||||
auto& targetSurface = camInstance->queue_targetSurfaces.front();
|
||||
g_cameraHandlerParam->type = 0;
|
||||
g_cameraHandlerParam->field1 = targetSurface.surfacePtr.GetMPTR();
|
||||
g_cameraHandlerParam->field2 = targetSurface.surfaceSize;
|
||||
g_cameraHandlerParam->field3 = 0;
|
||||
cemu_assert_debug(camInstance->handlerFunc != MPTR_NULL);
|
||||
camInstance->queue_targetSurfaces.pop();
|
||||
_lock.unlock();
|
||||
PPCCoreCallback(camInstance->handlerFunc, g_cameraHandlerParam.GetPtr());
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
|
||||
sint32 CAMInit(uint32 cameraId, CAMInitInfo_t* camInitInfo, uint32be* error)
|
||||
{
|
||||
CameraInstance* camInstance = new CameraInstance(camInitInfo->width, camInitInfo->height, camInitInfo->handlerFuncPtr);
|
||||
|
||||
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
|
||||
if (g_cameraCounter == 0)
|
||||
{
|
||||
coreinit::OSCreateAlarm(g_alarm_camera.GetPtr());
|
||||
coreinit::OSSetPeriodicAlarm(g_alarm_camera.GetPtr(), coreinit::coreinit_getOSTime(), (uint64)ESPRESSO_TIMER_CLOCK / 60ull, RPLLoader_MakePPCCallable(ppcCAMUpdate60));
|
||||
}
|
||||
g_cameraCounter++;
|
||||
|
||||
return camInstance->handle;
|
||||
}
|
||||
|
||||
sint32 CAMExit(sint32 camHandle)
|
||||
{
|
||||
CameraInstance* camInstance = GetCameraInstanceByHandle(camHandle);
|
||||
if (!camInstance)
|
||||
return CAM_ERROR_INVALID_HANDLE;
|
||||
CAMClose(camHandle);
|
||||
delete camInstance;
|
||||
|
||||
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
|
||||
g_cameraCounter--;
|
||||
if (g_cameraCounter == 0)
|
||||
coreinit::OSCancelAlarm(g_alarm_camera.GetPtr());
|
||||
return CAM_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
sint32 CAMOpen(sint32 camHandle)
|
||||
{
|
||||
CameraInstance* camInstance = GetCameraInstanceByHandle(camHandle);
|
||||
if (!camInstance)
|
||||
return CAM_ERROR_INVALID_HANDLE;
|
||||
camInstance->OpenCam();
|
||||
return CAM_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
sint32 CAMClose(sint32 camHandle)
|
||||
{
|
||||
CameraInstance* camInstance = GetCameraInstanceByHandle(camHandle);
|
||||
if (!camInstance)
|
||||
return CAM_ERROR_INVALID_HANDLE;
|
||||
camInstance->CloseCam();
|
||||
return CAM_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
sint32 CAMSubmitTargetSurface(sint32 camHandle, CAMTargetSurface* targetSurface)
|
||||
{
|
||||
CameraInstance* camInstance = GetCameraInstanceByHandle(camHandle);
|
||||
if (!camInstance)
|
||||
return CAM_ERROR_INVALID_HANDLE;
|
||||
|
||||
camInstance->QueueTargetSurface(targetSurface);
|
||||
|
||||
return CAM_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
g_cameraCounter = 0;
|
||||
}
|
||||
|
||||
void load()
|
||||
{
|
||||
reset();
|
||||
cafeExportRegister("camera", CAMGetMemReq, LogType::Placeholder);
|
||||
cafeExportRegister("camera", CAMCheckMemSegmentation, LogType::Placeholder);
|
||||
cafeExportRegister("camera", CAMInit, LogType::Placeholder);
|
||||
cafeExportRegister("camera", CAMExit, LogType::Placeholder);
|
||||
cafeExportRegister("camera", CAMOpen, LogType::Placeholder);
|
||||
cafeExportRegister("camera", CAMClose, LogType::Placeholder);
|
||||
cafeExportRegister("camera", CAMSubmitTargetSurface, LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
10
src/Cafe/OS/libs/camera/camera.h
Normal file
10
src/Cafe/OS/libs/camera/camera.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
namespace camera
|
||||
{
|
||||
|
||||
sint32 CAMOpen(sint32 camHandle);
|
||||
sint32 CAMClose(sint32 camHandle);
|
||||
|
||||
void load();
|
||||
};
|
||||
378
src/Cafe/OS/libs/coreinit/coreinit.cpp
Normal file
378
src/Cafe/OS/libs/coreinit/coreinit.cpp
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Common/SysAllocator.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
|
||||
|
||||
// includes for Initialize coreinit submodules
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_BSP.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Scheduler.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Atomic.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_OverlayArena.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_GHS.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_HWInterface.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Memory.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_IM.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_LockedCache.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MemoryMapping.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_IPC.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_IPCBuf.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Coroutine.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_OSScreen.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_SystemInfo.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_SysHeap.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MCP.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MPQueue.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_FS.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h"
|
||||
|
||||
coreinitData_t* gCoreinitData = NULL;
|
||||
|
||||
sint32 ScoreStackTrace(OSThread_t* thread, MPTR sp)
|
||||
{
|
||||
uint32 stackMinAddr = _swapEndianU32(thread->stackEnd);
|
||||
uint32 stackMaxAddr = _swapEndianU32(thread->stackBase);
|
||||
|
||||
sint32 score = 0;
|
||||
uint32 currentStackPtr = sp;
|
||||
for (sint32 i = 0; i < 50; i++)
|
||||
{
|
||||
uint32 nextStackPtr = memory_readU32(currentStackPtr);
|
||||
if (nextStackPtr < currentStackPtr)
|
||||
break;
|
||||
if (nextStackPtr < stackMinAddr || nextStackPtr > stackMaxAddr)
|
||||
break;
|
||||
if ((nextStackPtr & 3) != 0)
|
||||
break;
|
||||
score += 10;
|
||||
|
||||
uint32 returnAddress = 0;
|
||||
returnAddress = memory_readU32(nextStackPtr + 4);
|
||||
//cemuLog_log(LogType::Force, fmt::format("SP {0:08x} ReturnAddress {1:08x}", nextStackPtr, returnAddress));
|
||||
if (returnAddress > 0 && returnAddress < 0x10000000 && (returnAddress&3) == 0)
|
||||
score += 5; // within code region
|
||||
else
|
||||
score -= 5;
|
||||
|
||||
currentStackPtr = nextStackPtr;
|
||||
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
void DebugLogStackTrace(OSThread_t* thread, MPTR sp)
|
||||
{
|
||||
// sp might not point to a valid stackframe
|
||||
// scan stack and evaluate which sp is most likely the beginning of the stackframe
|
||||
|
||||
// scan 0x400 bytes
|
||||
sint32 highestScore = -1;
|
||||
uint32 highestScoreSP = sp;
|
||||
for (sint32 i = 0; i < 0x100; i++)
|
||||
{
|
||||
uint32 sampleSP = sp + i * 4;
|
||||
sint32 score = ScoreStackTrace(thread, sampleSP);
|
||||
if (score > highestScore)
|
||||
{
|
||||
highestScore = score;
|
||||
highestScoreSP = sampleSP;
|
||||
}
|
||||
}
|
||||
|
||||
if (highestScoreSP != sp)
|
||||
cemuLog_log(LogType::Force, fmt::format("Trace starting at SP {0:08x} r1 = {1:08x}", highestScoreSP, sp));
|
||||
else
|
||||
cemuLog_log(LogType::Force, fmt::format("Trace starting at SP/r1 {0:08x}", highestScoreSP));
|
||||
|
||||
// print stack trace
|
||||
uint32 currentStackPtr = highestScoreSP;
|
||||
uint32 stackMinAddr = _swapEndianU32(thread->stackEnd);
|
||||
uint32 stackMaxAddr = _swapEndianU32(thread->stackBase);
|
||||
for (sint32 i = 0; i < 20; i++)
|
||||
{
|
||||
uint32 nextStackPtr = memory_readU32(currentStackPtr);
|
||||
if (nextStackPtr < currentStackPtr)
|
||||
break;
|
||||
if (nextStackPtr < stackMinAddr || nextStackPtr > stackMaxAddr)
|
||||
break;
|
||||
|
||||
uint32 returnAddress = 0;
|
||||
returnAddress = memory_readU32(nextStackPtr + 4);
|
||||
cemuLog_log(LogType::Force, fmt::format("SP {0:08x} ReturnAddr {1:08x}", nextStackPtr, returnAddress));
|
||||
|
||||
currentStackPtr = nextStackPtr;
|
||||
}
|
||||
}
|
||||
|
||||
void coreinitExport_OSPanic(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("OSPanic!\n");
|
||||
debug_printf("File: %s:%d\n", memory_getPointerFromVirtualOffset(hCPU->gpr[3]), hCPU->gpr[4]);
|
||||
debug_printf("Msg: %s\n", memory_getPointerFromVirtualOffset(hCPU->gpr[5]));
|
||||
DebugLogStackTrace(coreinit::OSGetCurrentThread(), coreinit::OSGetStackPointer());
|
||||
#ifndef PUBLIC_RELEASE
|
||||
assert_dbg();
|
||||
while (true) std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
#endif
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32be name;
|
||||
/* +0x04 */ uint32be fileType; // 2 = font
|
||||
/* +0x08 */ uint32be kernelFilenamePtr;
|
||||
/* +0x0C */ MEMPTR<void> data;
|
||||
/* +0x10 */ uint32be size;
|
||||
/* +0x14 */ uint32be ukn14;
|
||||
/* +0x18 */ uint32be ukn18;
|
||||
}coreinitShareddataEntry_t;
|
||||
|
||||
static_assert(sizeof(coreinitShareddataEntry_t) == 0x1C, "");
|
||||
|
||||
uint8* extractCafeDefaultFont(sint32* size);
|
||||
|
||||
MPTR placeholderFont = MPTR_NULL;
|
||||
sint32 placeholderFontSize = 0;
|
||||
|
||||
void coreinitExport_OSGetSharedData(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 sharedAreaId
|
||||
// r4 flags
|
||||
// r5 areaPtrPtr
|
||||
// r6 areaSizePtr
|
||||
|
||||
// on real Wii U hw/sw there is a list of shared area entries starting at offset +0xF8000000
|
||||
// properly formated (each entry is 0x1C bytes) it looks like this:
|
||||
// FF CA FE 01 00 00 00 02 FF E8 47 AC F8 00 00 70 00 C8 0D 4C 00 00 00 00 FF FF FF FC
|
||||
// FF CA FE 02 00 00 00 02 FF E8 47 B7 F8 C8 0D C0 00 22 7E B4 00 00 00 00 00 00 11 D5
|
||||
// FF CA FE 03 00 00 00 02 FF E8 47 A0 F8 EA 8C 80 00 25 44 E0 00 00 00 00 FF A0 00 00
|
||||
// FF CA FE 04 00 00 00 02 FF E8 47 C2 F9 0F D1 60 00 7D 93 5C 00 00 00 00 FF FF FF FC
|
||||
|
||||
uint32 sharedAreaId = hCPU->gpr[3];
|
||||
|
||||
coreinitShareddataEntry_t* shareddataTable = (coreinitShareddataEntry_t*)memory_getPointerFromVirtualOffset(MEMORY_SHAREDDATA_AREA_ADDR);
|
||||
|
||||
uint32 name = 0xFFCAFE01 + sharedAreaId;
|
||||
for (sint32 i = 0; i < 4; i++)
|
||||
{
|
||||
if ((uint32)shareddataTable[i].name == name)
|
||||
{
|
||||
memory_writeU32(hCPU->gpr[5], shareddataTable[i].data.GetMPTR());
|
||||
memory_writeU32(hCPU->gpr[6], (uint32)shareddataTable[i].size);
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// some games require a valid result or they will crash, return a pointer to our placeholder font
|
||||
forceLog_printf("OSGetSharedData() called by game but no shareddata fonts loaded. Use placeholder font");
|
||||
if (placeholderFont == MPTR_NULL)
|
||||
{
|
||||
// load and then return placeholder font
|
||||
uint8* placeholderFontPtr = extractCafeDefaultFont(&placeholderFontSize);
|
||||
placeholderFont = coreinit_allocFromSysArea(placeholderFontSize, 256);
|
||||
if (placeholderFont == MPTR_NULL)
|
||||
forceLog_printf("Failed to alloc placeholder font sys memory");
|
||||
memcpy(memory_getPointerFromVirtualOffset(placeholderFont), placeholderFontPtr, placeholderFontSize);
|
||||
free(placeholderFontPtr);
|
||||
}
|
||||
// return placeholder font
|
||||
memory_writeU32(hCPU->gpr[5], placeholderFont);
|
||||
memory_writeU32(hCPU->gpr[6], placeholderFontSize);
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MPTR getDriverName;
|
||||
MPTR ukn04;
|
||||
MPTR onAcquiredForeground;
|
||||
MPTR onReleaseForeground;
|
||||
MPTR ukn10;
|
||||
}OSDriverCallbacks_t;
|
||||
|
||||
void coreinitExport_OSDriver_Register(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
#ifndef PUBLIC_RELEASE
|
||||
forceLog_printf("OSDriver_Register(0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->gpr[8]);
|
||||
#endif
|
||||
OSDriverCallbacks_t* driverCallbacks = (OSDriverCallbacks_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[5]);
|
||||
|
||||
// todo
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
sint32 OSGetCoreId()
|
||||
{
|
||||
return PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
}
|
||||
|
||||
uint32 OSGetCoreCount()
|
||||
{
|
||||
return Espresso::CORE_COUNT;
|
||||
}
|
||||
|
||||
uint32 OSIsDebuggerInitialized()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 OSGetConsoleType()
|
||||
{
|
||||
return 0x03000050;
|
||||
}
|
||||
|
||||
uint32 OSGetMainCoreId()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool OSIsMainCore()
|
||||
{
|
||||
return OSGetCoreId() == OSGetMainCoreId();
|
||||
}
|
||||
|
||||
uint32 OSGetStackPointer()
|
||||
{
|
||||
return ppcInterpreterCurrentInstance->gpr[1];
|
||||
}
|
||||
|
||||
void coreinitExport_ENVGetEnvironmentVariable(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("ENVGetEnvironmentVariable(\"%s\",0x08x,0x%x)\n", (char*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]), hCPU->gpr[4], hCPU->gpr[5]);
|
||||
char* envKeyStr = (char*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
char* outputString = (char*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
|
||||
sint32 outputStringMaxLen = (sint32)hCPU->gpr[5];
|
||||
// also return the string "" just in case
|
||||
if (outputStringMaxLen > 0)
|
||||
{
|
||||
outputString[0] = '\0';
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
}
|
||||
|
||||
void coreinit_exit(uint32 r)
|
||||
{
|
||||
forceLog_printf("coreinit.exit(%d)", r);
|
||||
cemu_assert_debug(false);
|
||||
// never return
|
||||
while (true) std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
bool OSIsOffBoot()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 OSGetBootPMFlags()
|
||||
{
|
||||
forceLogDebug_printf("OSGetBootPMFlags() - placeholder");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 OSGetSystemMode()
|
||||
{
|
||||
forceLogDebug_printf("OSGetSystemMode() - placeholder");
|
||||
// if this returns 2, barista softlocks shortly after boot
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InitializeCore()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSGetCoreId, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSGetCoreCount, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSIsDebuggerInitialized, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSGetConsoleType, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSGetMainCoreId, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSIsMainCore, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSGetStackPointer, LogType::CoreinitThread);
|
||||
|
||||
osLib_addFunction("coreinit", "ENVGetEnvironmentVariable", coreinitExport_ENVGetEnvironmentVariable);
|
||||
|
||||
cafeExportRegisterFunc(coreinit_exit, "coreinit", "exit", LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSIsOffBoot, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSGetBootPMFlags, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSGetSystemMode, LogType::CoreinitThread);
|
||||
}
|
||||
};
|
||||
|
||||
void coreinit_load()
|
||||
{
|
||||
coreinit::InitializeCore();
|
||||
coreinit::InitializeSchedulerLock();
|
||||
coreinit::InitializeSysHeap();
|
||||
|
||||
// allocate coreinit global data
|
||||
gCoreinitData = (coreinitData_t*)memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sizeof(coreinitData_t), 32));
|
||||
memset(gCoreinitData, 0x00, sizeof(coreinitData_t));
|
||||
|
||||
// coreinit weak links
|
||||
osLib_addVirtualPointer("coreinit", "MEMAllocFromDefaultHeap", memory_getVirtualOffsetFromPointer(&gCoreinitData->MEMAllocFromDefaultHeap));
|
||||
osLib_addVirtualPointer("coreinit", "MEMAllocFromDefaultHeapEx", memory_getVirtualOffsetFromPointer(&gCoreinitData->MEMAllocFromDefaultHeapEx));
|
||||
osLib_addVirtualPointer("coreinit", "MEMFreeToDefaultHeap", memory_getVirtualOffsetFromPointer(&gCoreinitData->MEMFreeToDefaultHeap));
|
||||
osLib_addVirtualPointer("coreinit", "__atexit_cleanup", memory_getVirtualOffsetFromPointer(&gCoreinitData->__atexit_cleanup));
|
||||
osLib_addVirtualPointer("coreinit", "__stdio_cleanup", memory_getVirtualOffsetFromPointer(&gCoreinitData->__stdio_cleanup));
|
||||
osLib_addVirtualPointer("coreinit", "__cpp_exception_cleanup_ptr", memory_getVirtualOffsetFromPointer(&gCoreinitData->__cpp_exception_cleanup_ptr));
|
||||
osLib_addVirtualPointer("coreinit", "__cpp_exception_init_ptr", memory_getVirtualOffsetFromPointer(&gCoreinitData->__cpp_exception_init_ptr));
|
||||
|
||||
// init GHS and threads
|
||||
coreinit::PrepareGHSRuntime();
|
||||
coreinit::InitializeThread();
|
||||
|
||||
// reset threads
|
||||
activeThreadCount = 0;
|
||||
// init submodules
|
||||
coreinit::InitializeMEM();
|
||||
coreinit::InitializeMEMFrmHeap();
|
||||
coreinit::InitializeMEMUnitHeap();
|
||||
coreinit::InitializeMEMBlockHeap();
|
||||
coreinit::InitializeFG();
|
||||
coreinit::InitializeBSP();
|
||||
coreinit::InitializeMCP();
|
||||
coreinit::InitializeOverlayArena();
|
||||
coreinit::InitializeDynLoad();
|
||||
coreinit::InitializeGHS();
|
||||
coreinit::InitializeHWInterface();
|
||||
coreinit::InitializeAtomic();
|
||||
coreinit::InitializeMemory();
|
||||
coreinit::InitializeIM();
|
||||
coreinit::InitializeLC();
|
||||
coreinit::InitializeMP();
|
||||
coreinit::InitializeTimeAndCalendar();
|
||||
coreinit::InitializeAlarm();
|
||||
coreinit::InitializeFS();
|
||||
coreinit::InitializeSystemInfo();
|
||||
coreinit::InitializeConcurrency();
|
||||
coreinit::InitializeSpinlock();
|
||||
coreinit::InitializeMessageQueue();
|
||||
coreinit::InitializeIPC();
|
||||
coreinit::InitializeIPCBuf();
|
||||
coreinit::InitializeCodeGen();
|
||||
coreinit::InitializeCoroutine();
|
||||
coreinit::InitializeOSScreen();
|
||||
|
||||
// legacy mem stuff
|
||||
coreinit::expheap_load();
|
||||
|
||||
// misc exports
|
||||
coreinit::miscInit();
|
||||
osLib_addFunction("coreinit", "OSGetSharedData", coreinitExport_OSGetSharedData);
|
||||
osLib_addFunction("coreinit", "UCReadSysConfig", coreinitExport_UCReadSysConfig);
|
||||
osLib_addFunction("coreinit", "OSDriver_Register", coreinitExport_OSDriver_Register);
|
||||
|
||||
// async callbacks
|
||||
InitializeAsyncCallback();
|
||||
}
|
||||
48
src/Cafe/OS/libs/coreinit/coreinit.h
Normal file
48
src/Cafe/OS/libs/coreinit/coreinit.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#include "Cafe/HW/Espresso/Const.h"
|
||||
|
||||
#define PPC_CORE_COUNT (Espresso::CORE_COUNT)
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MessageQueue.h"
|
||||
|
||||
// async callback helper
|
||||
|
||||
void InitializeAsyncCallback();
|
||||
void coreinitAsyncCallback_add(MPTR functionMPTR, uint32 numParameters, uint32 r3 = 0, uint32 r4 = 0, uint32 r5 = 0, uint32 r6 = 0, uint32 r7 = 0, uint32 r8 = 0, uint32 r9 = 0, uint32 r10 = 0);
|
||||
void coreinitAsyncCallback_addWithLock(MPTR functionMPTR, uint32 numParameters, uint32 r3 = 0, uint32 r4 = 0, uint32 r5 = 0, uint32 r6 = 0, uint32 r7 = 0, uint32 r8 = 0, uint32 r9 = 0, uint32 r10 = 0);
|
||||
|
||||
// misc
|
||||
|
||||
void coreinit_load();
|
||||
|
||||
// coreinit shared memory
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MEMPTR<void> MEMAllocFromDefaultHeap;
|
||||
MEMPTR<void> MEMAllocFromDefaultHeapEx;
|
||||
MEMPTR<void> MEMFreeToDefaultHeap;
|
||||
MPTR __atexit_cleanup;
|
||||
MPTR __cpp_exception_init_ptr;
|
||||
MPTR __cpp_exception_cleanup_ptr;
|
||||
MPTR __stdio_cleanup;
|
||||
}coreinitData_t;
|
||||
|
||||
extern coreinitData_t* gCoreinitData;
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
|
||||
|
||||
// coreinit init
|
||||
void coreinit_start(PPCInterpreter_t* hCPU);
|
||||
|
||||
MPTR OSAllocFromSystem(uint32 size, uint32 alignment);
|
||||
void OSFreeToSystem(MPTR mem);
|
||||
|
||||
// above is all the legacy stuff. New code uses namespaces
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
sint32 OSGetCoreId();
|
||||
uint32 OSGetCoreCount();
|
||||
uint32 OSGetStackPointer();
|
||||
};
|
||||
359
src/Cafe/OS/libs/coreinit/coreinit_Alarm.cpp
Normal file
359
src/Cafe/OS/libs/coreinit/coreinit_Alarm.cpp
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/HW/Espresso/PPCCallback.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
|
||||
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
|
||||
// #define ALARM_LOGGING
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
SysAllocator<OSEvent> g_alarmEvent;
|
||||
|
||||
SysAllocator<OSThread_t> g_alarmThread;
|
||||
SysAllocator<uint8, 1024 * 128> _g_alarmThreadStack;
|
||||
SysAllocator<char, 32> _g_alarmThreadName;
|
||||
|
||||
|
||||
class OSHostAlarm
|
||||
{
|
||||
public:
|
||||
OSHostAlarm(uint64 nextFire, uint64 period, void(*callbackFunc)(uint64 currentTick, void* context), void* context) : m_nextFire(nextFire), m_period(period), m_callbackFunc(callbackFunc), m_context(context)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock()); // must hold lock
|
||||
auto r = g_activeAlarmList.emplace(this);
|
||||
cemu_assert_debug(r.second); // check if insertion was successful
|
||||
m_isActive = true;
|
||||
updateEarliestAlarmAtomic();
|
||||
}
|
||||
|
||||
~OSHostAlarm()
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock()); // must hold lock
|
||||
if (m_isActive)
|
||||
{
|
||||
g_activeAlarmList.erase(g_activeAlarmList.find(this));
|
||||
updateEarliestAlarmAtomic();
|
||||
}
|
||||
}
|
||||
|
||||
uint64 getFireTick() const
|
||||
{
|
||||
return m_nextFire;
|
||||
}
|
||||
|
||||
void triggerAlarm(uint64 currentTick)
|
||||
{
|
||||
m_callbackFunc(currentTick, m_context);
|
||||
}
|
||||
|
||||
static void updateEarliestAlarmAtomic()
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
if (!g_activeAlarmList.empty())
|
||||
{
|
||||
auto firstAlarm = g_activeAlarmList.begin();
|
||||
g_soonestAlarm = (*firstAlarm)->m_nextFire;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_soonestAlarm = std::numeric_limits<uint64>::max();
|
||||
}
|
||||
}
|
||||
|
||||
static void updateAlarms(uint64 currentTick)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
if (g_activeAlarmList.empty())
|
||||
return;
|
||||
|
||||
// debug begin
|
||||
#ifndef PUBLIC_RELEASE
|
||||
uint64 prevTick = 0;
|
||||
auto itr = g_activeAlarmList.begin();
|
||||
while (itr != g_activeAlarmList.end())
|
||||
{
|
||||
uint64 t = (*itr)->m_nextFire;
|
||||
if (t < prevTick)
|
||||
cemu_assert_suspicious();
|
||||
prevTick = t;
|
||||
++itr;
|
||||
}
|
||||
#endif
|
||||
// debug end
|
||||
while (true)
|
||||
{
|
||||
auto firstAlarm = g_activeAlarmList.begin();
|
||||
if (currentTick >= (*firstAlarm)->m_nextFire)
|
||||
{
|
||||
OSHostAlarm* alarm = *firstAlarm;
|
||||
g_activeAlarmList.erase(firstAlarm);
|
||||
alarm->triggerAlarm(currentTick);
|
||||
// if periodic alarm then requeue
|
||||
if (alarm->m_period > 0)
|
||||
{
|
||||
alarm->m_nextFire += alarm->m_period;
|
||||
g_activeAlarmList.emplace(alarm);
|
||||
}
|
||||
else
|
||||
alarm->m_isActive = false;
|
||||
updateEarliestAlarmAtomic();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint64 getNextFire() const
|
||||
{
|
||||
return m_nextFire;
|
||||
}
|
||||
|
||||
static bool quickCheckForAlarm(uint64 currentTick)
|
||||
{
|
||||
// fast way to check if any alarm was triggered without requiring scheduler lock
|
||||
return currentTick >= g_soonestAlarm;
|
||||
}
|
||||
|
||||
public:
|
||||
struct ComparatorFireTime
|
||||
{
|
||||
bool operator ()(OSHostAlarm* const & p1, OSHostAlarm* const & p2) const
|
||||
{
|
||||
auto p1Fire = p1->getNextFire();
|
||||
auto p2Fire = p2->getNextFire();
|
||||
if (p1Fire == p2Fire)
|
||||
return (uintptr_t)p1 < (uintptr_t)p2; // if time is equal, differ by pointer (to allow storing multiple alarms with same firing time)
|
||||
return p1Fire < p2Fire;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
uint64 m_nextFire;
|
||||
uint64 m_period; // if zero then repeat is disabled
|
||||
bool m_isActive{ false };
|
||||
|
||||
void (*m_callbackFunc)(uint64 currentTick, void* context);
|
||||
void* m_context;
|
||||
|
||||
static std::set<class OSHostAlarm*, ComparatorFireTime> g_activeAlarmList;
|
||||
static std::atomic_uint64_t g_soonestAlarm;
|
||||
};
|
||||
|
||||
|
||||
std::set<class OSHostAlarm*, OSHostAlarm::ComparatorFireTime> OSHostAlarm::g_activeAlarmList;
|
||||
std::atomic_uint64_t OSHostAlarm::g_soonestAlarm{};
|
||||
|
||||
OSHostAlarm* OSHostAlarmCreate(uint64 nextFire, uint64 period, void(*callbackFunc)(uint64 currentTick, void* context), void* context)
|
||||
{
|
||||
OSHostAlarm* hostAlarm = new OSHostAlarm(nextFire, period, callbackFunc, context);
|
||||
return hostAlarm;
|
||||
}
|
||||
|
||||
void OSHostAlarmDestroy(OSHostAlarm* hostAlarm)
|
||||
{
|
||||
delete hostAlarm;
|
||||
}
|
||||
|
||||
void alarm_update()
|
||||
{
|
||||
cemu_assert_debug(!__OSHasSchedulerLock());
|
||||
uint64 currentTick = coreinit::coreinit_getOSTime();
|
||||
if (!OSHostAlarm::quickCheckForAlarm(currentTick))
|
||||
return;
|
||||
__OSLockScheduler();
|
||||
OSHostAlarm::updateAlarms(currentTick);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
/* alarm API */
|
||||
|
||||
void OSCreateAlarm(OSAlarm_t* alarm)
|
||||
{
|
||||
memset(alarm, 0, sizeof(OSAlarm_t));
|
||||
alarm->setMagic();
|
||||
}
|
||||
|
||||
void coreinitExport_OSCreateAlarmEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
OSAlarm_t* OSAlarm = (OSAlarm_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
OSCreateAlarm(OSAlarm);
|
||||
OSAlarm->name = _swapEndianU32(hCPU->gpr[4]);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
std::unordered_map<OSAlarm_t*, OSHostAlarm*> g_activeAlarms;
|
||||
|
||||
bool OSCancelAlarm(OSAlarm_t* alarm)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
bool alarmWasActive = false;
|
||||
auto itr = g_activeAlarms.find(alarm);
|
||||
if (itr != g_activeAlarms.end())
|
||||
{
|
||||
OSHostAlarmDestroy(itr->second);
|
||||
g_activeAlarms.erase(itr);
|
||||
alarmWasActive = true;
|
||||
}
|
||||
|
||||
__OSUnlockScheduler();
|
||||
return alarmWasActive;
|
||||
}
|
||||
|
||||
void __OSHostAlarmTriggered(uint64 currentTick, void* context)
|
||||
{
|
||||
#ifdef ALARM_LOGGING
|
||||
cemuLog_log(LogType::Force, "[Alarm] Alarm ready and alarm thread signalled. Current tick: {}", currentTick);
|
||||
#endif
|
||||
OSSignalEventInternal(g_alarmEvent.GetPtr());
|
||||
}
|
||||
|
||||
void __OSInitiateAlarm(OSAlarm_t* alarm, uint64 startTime, uint64 period, MPTR handlerFunc, bool isPeriodic)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
|
||||
uint64 nextTime = startTime;
|
||||
|
||||
#ifdef ALARM_LOGGING
|
||||
double periodInMS = (double)period * 1000.0 / (double)EspressoTime::GetTimerClock();
|
||||
cemuLog_log(LogType::Force, "[Alarm] Start alarm 0x{:08x}. Func 0x{:08x}. Period: {}ms", MEMPTR(alarm).GetMPTR(), handlerFunc, periodInMS);
|
||||
#endif
|
||||
|
||||
if (isPeriodic)
|
||||
{
|
||||
cemu_assert_debug(period != 0);
|
||||
if (period == 0)
|
||||
return;
|
||||
|
||||
uint64 currentTime = coreinit_getOSTime();
|
||||
|
||||
uint64 ticksSinceStart = currentTime - startTime;
|
||||
uint64 numPeriods = ticksSinceStart / period;
|
||||
|
||||
nextTime = startTime + (numPeriods + 1ull) * period;
|
||||
|
||||
alarm->startTime = _swapEndianU64(startTime);
|
||||
alarm->nextTime = _swapEndianU64(nextTime);
|
||||
alarm->period = _swapEndianU64(period);
|
||||
alarm->handler = _swapEndianU32(handlerFunc);
|
||||
}
|
||||
else
|
||||
{
|
||||
alarm->nextTime = _swapEndianU64(startTime);
|
||||
alarm->period = 0;
|
||||
alarm->handler = _swapEndianU32(handlerFunc);
|
||||
}
|
||||
|
||||
auto existingAlarmItr = g_activeAlarms.find(alarm);
|
||||
if (existingAlarmItr != g_activeAlarms.end())
|
||||
{
|
||||
// delete existing alarm
|
||||
forceLogDebug_printf("__OSInitiateAlarm() called on alarm which was already active");
|
||||
OSHostAlarmDestroy(existingAlarmItr->second);
|
||||
g_activeAlarms.erase(existingAlarmItr);
|
||||
}
|
||||
|
||||
g_activeAlarms[alarm] = OSHostAlarmCreate(nextTime, period, __OSHostAlarmTriggered, nullptr);
|
||||
}
|
||||
|
||||
void OSSetAlarm(OSAlarm_t* alarm, uint64 delayInTicks, MPTR handlerFunc)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
__OSInitiateAlarm(alarm, coreinit_getOSTime() + delayInTicks, 0, handlerFunc, false);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void OSSetPeriodicAlarm(OSAlarm_t* alarm, uint64 nextFire, uint64 period, MPTR handlerFunc)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
__OSInitiateAlarm(alarm, nextFire, period, handlerFunc, true);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void OSSetAlarmUserData(OSAlarm_t* alarm, uint32 userData)
|
||||
{
|
||||
alarm->userData = _swapEndianU32(userData);
|
||||
}
|
||||
|
||||
void coreinitExport_OSGetAlarmUserData(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
OSAlarm_t* OSAlarmBE = (OSAlarm_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
MPTR userData = _swapEndianU32(OSAlarmBE->userData);
|
||||
osLib_returnFromFunction(hCPU, userData);
|
||||
}
|
||||
|
||||
void OSAlarm_resetAll()
|
||||
{
|
||||
cemu_assert_debug(g_activeAlarms.empty());
|
||||
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
|
||||
void _OSAlarmThread(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
OSWaitEvent(g_alarmEvent.GetPtr());
|
||||
uint64 currentTick = coreinit_getOSTime();
|
||||
while (true)
|
||||
{
|
||||
// get alarm to fire
|
||||
OSAlarm_t* alarm = nullptr;
|
||||
__OSLockScheduler();
|
||||
auto itr = g_activeAlarms.begin();
|
||||
while(itr != g_activeAlarms.end())
|
||||
{
|
||||
if (currentTick >= _swapEndianU64(itr->first->nextTime))
|
||||
{
|
||||
alarm = itr->first;
|
||||
if (alarm->period == 0)
|
||||
{
|
||||
// end alarm
|
||||
g_activeAlarms.erase(itr);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
alarm->nextTime = _swapEndianU64(_swapEndianU64(alarm->nextTime) + _swapEndianU64(alarm->period));
|
||||
}
|
||||
break;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
__OSUnlockScheduler();
|
||||
if (!alarm)
|
||||
break;
|
||||
// do callback for alarm
|
||||
#ifdef ALARM_LOGGING
|
||||
double periodInMS = (double)_swapEndianU64(alarm->period) * 1000.0 / (double)EspressoTime::GetTimerClock();
|
||||
cemuLog_log(LogType::Force, "[Alarm] Callback 0x{:08x} for alarm 0x{:08x}. Current tick: {}. Period: {}ms", _swapEndianU32(alarm->handler), MEMPTR(alarm).GetMPTR(), currentTick, periodInMS);
|
||||
#endif
|
||||
PPCCoreCallback(_swapEndianU32(alarm->handler), alarm, &(g_alarmThread.GetPtr()->context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeAlarm()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSCreateAlarm, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSCancelAlarm, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSSetAlarm, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSSetPeriodicAlarm, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", OSSetAlarmUserData, LogType::Placeholder);
|
||||
|
||||
osLib_addFunction("coreinit", "OSCreateAlarmEx", coreinitExport_OSCreateAlarmEx);
|
||||
osLib_addFunction("coreinit", "OSGetAlarmUserData", coreinitExport_OSGetAlarmUserData);
|
||||
|
||||
// init event
|
||||
OSInitEvent(g_alarmEvent.GetPtr(), OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_AUTO);
|
||||
|
||||
// create alarm callback handler thread
|
||||
coreinit::OSCreateThreadType(g_alarmThread.GetPtr(), RPLLoader_MakePPCCallable(_OSAlarmThread), 0, nullptr, _g_alarmThreadStack.GetPtr() + _g_alarmThreadStack.GetByteSize(), (sint32)_g_alarmThreadStack.GetByteSize(), 0, 0x7, OSThread_t::THREAD_TYPE::TYPE_IO);
|
||||
OSResumeThread(g_alarmThread.GetPtr());
|
||||
strcpy(_g_alarmThreadName.GetPtr(), "Alarm Thread");
|
||||
coreinit::OSSetThreadName(g_alarmThread.GetPtr(), _g_alarmThreadName.GetPtr());
|
||||
}
|
||||
}
|
||||
54
src/Cafe/OS/libs/coreinit/coreinit_Alarm.h
Normal file
54
src/Cafe/OS/libs/coreinit/coreinit_Alarm.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
class OSHostAlarm;
|
||||
OSHostAlarm* OSHostAlarmCreate(uint64 nextFire, uint64 period, void(*callbackFunc)(uint64 currentTick, void* context), void* context);
|
||||
void OSHostAlarmDestroy(OSHostAlarm* hostAlarm);
|
||||
|
||||
struct OSAlarm_t
|
||||
{
|
||||
/* +0x00 */ betype<uint32> magic;
|
||||
/* +0x04 */ MPTR_UINT8 name;
|
||||
/* +0x08 */ uint32 ukn08;
|
||||
/* +0x0C */ MPTR handler;
|
||||
/* +0x10 */ uint32 ukn10;
|
||||
/* +0x14 */ uint32 padding14;
|
||||
/* +0x18 */ uint64 nextTime; // next fire time
|
||||
/* +0x20 */ MPTR prev; // pointer to OSAlarm
|
||||
/* +0x24 */ MPTR next; // pointer to OSAlarm
|
||||
/* +0x28 */ uint64 period; // period (zero for non-periodic timer)
|
||||
/* +0x30 */ uint64 startTime; // period start
|
||||
/* +0x38 */ MPTR userData;
|
||||
/* +0x3C */ uint32 ukn3C;
|
||||
/* +0x40 */ OSThreadQueue uknThreadQueue;
|
||||
/* +0x50 */ MPTR alarmQueue;
|
||||
/* +0x54 */ MPTR ukn54;
|
||||
|
||||
void setMagic()
|
||||
{
|
||||
magic = (uint32)'aLrM';
|
||||
}
|
||||
|
||||
bool checkMagic()
|
||||
{
|
||||
return magic == (uint32)'aLrM';
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSAlarm_t) == 0x58);
|
||||
|
||||
void OSCreateAlarm(OSAlarm_t* alarm);
|
||||
bool OSCancelAlarm(OSAlarm_t* alarm);
|
||||
void OSSetAlarm(OSAlarm_t* alarm, uint64 time, MPTR handlerFunc);
|
||||
void OSSetAlarmUserData(OSAlarm_t* alarm, uint32 userData);
|
||||
void OSSetPeriodicAlarm(OSAlarm_t* OSAlarm, uint64 startTick, uint64 periodTick, MPTR OSAlarmHandler);
|
||||
|
||||
void OSAlarm_resetAll();
|
||||
|
||||
void alarm_update();
|
||||
|
||||
void InitializeAlarm();
|
||||
}
|
||||
139
src/Cafe/OS/libs/coreinit/coreinit_Atomic.cpp
Normal file
139
src/Cafe/OS/libs/coreinit/coreinit_Atomic.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include <atomic>
|
||||
#include "coreinit_Atomic.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
/* 32bit atomic operations */
|
||||
|
||||
uint32 OSSwapAtomic(std::atomic<uint32be>* mem, uint32 newValue)
|
||||
{
|
||||
uint32be _newValue = newValue;
|
||||
uint32be previousValue = mem->exchange(_newValue);
|
||||
return previousValue;
|
||||
}
|
||||
|
||||
bool OSCompareAndSwapAtomic(std::atomic<uint32be>* mem, uint32 compareValue, uint32 swapValue)
|
||||
{
|
||||
// seen in GTA3 homebrew port
|
||||
uint32be _compareValue = compareValue;
|
||||
uint32be _swapValue = swapValue;
|
||||
return mem->compare_exchange_strong(_compareValue, _swapValue);
|
||||
}
|
||||
|
||||
bool OSCompareAndSwapAtomicEx(std::atomic<uint32be>* mem, uint32 compareValue, uint32 swapValue, uint32be* previousValue)
|
||||
{
|
||||
// seen in GTA3 homebrew port
|
||||
uint32be _compareValue = compareValue;
|
||||
uint32be _swapValue = swapValue;
|
||||
bool r = mem->compare_exchange_strong(_compareValue, _swapValue);
|
||||
*previousValue = _compareValue;
|
||||
return r;
|
||||
}
|
||||
|
||||
uint32 OSAddAtomic(std::atomic<uint32be>* mem, uint32 adder)
|
||||
{
|
||||
uint32be knownValue;
|
||||
while (true)
|
||||
{
|
||||
uint32be knownValue = mem->load();
|
||||
uint32be newValue = knownValue + adder;
|
||||
if (mem->compare_exchange_strong(knownValue, newValue))
|
||||
break;
|
||||
}
|
||||
return knownValue;
|
||||
}
|
||||
|
||||
/* 64bit atomic operations */
|
||||
|
||||
uint64 OSSwapAtomic64(std::atomic<uint64be>* mem, uint64 newValue)
|
||||
{
|
||||
uint64be _newValue = newValue;
|
||||
uint64be previousValue = mem->exchange(_newValue);
|
||||
return previousValue;
|
||||
}
|
||||
|
||||
uint64 OSSetAtomic64(std::atomic<uint64be>* mem, uint64 newValue)
|
||||
{
|
||||
return OSSwapAtomic64(mem, newValue);
|
||||
}
|
||||
|
||||
uint64 OSGetAtomic64(std::atomic<uint64be>* mem)
|
||||
{
|
||||
return mem->load();
|
||||
}
|
||||
|
||||
uint64 OSAddAtomic64(std::atomic<uint64be>* mem, uint64 adder)
|
||||
{
|
||||
uint64be knownValue;
|
||||
while (true)
|
||||
{
|
||||
uint64be knownValue = mem->load();
|
||||
uint64be newValue = knownValue + adder;
|
||||
if (mem->compare_exchange_strong(knownValue, newValue))
|
||||
break;
|
||||
}
|
||||
return knownValue;
|
||||
}
|
||||
|
||||
uint64 OSAndAtomic64(std::atomic<uint64be>* mem, uint64 val)
|
||||
{
|
||||
uint64be knownValue;
|
||||
while (true)
|
||||
{
|
||||
uint64be knownValue = mem->load();
|
||||
uint64be newValue = knownValue & val;
|
||||
if (mem->compare_exchange_strong(knownValue, newValue))
|
||||
break;
|
||||
}
|
||||
return knownValue;
|
||||
}
|
||||
|
||||
uint64 OSOrAtomic64(std::atomic<uint64be>* mem, uint64 val)
|
||||
{
|
||||
uint64be knownValue;
|
||||
while (true)
|
||||
{
|
||||
uint64be knownValue = mem->load();
|
||||
uint64be newValue = knownValue | val;
|
||||
if (mem->compare_exchange_strong(knownValue, newValue))
|
||||
break;
|
||||
}
|
||||
return knownValue;
|
||||
}
|
||||
|
||||
bool OSCompareAndSwapAtomic64(std::atomic<uint64be>* mem, uint64 compareValue, uint64 swapValue)
|
||||
{
|
||||
uint64be _compareValue = compareValue;
|
||||
uint64be _swapValue = swapValue;
|
||||
return mem->compare_exchange_strong(_compareValue, _swapValue);
|
||||
}
|
||||
|
||||
bool OSCompareAndSwapAtomicEx64(std::atomic<uint64be>* mem, uint64 compareValue, uint64 swapValue, uint64be* previousValue)
|
||||
{
|
||||
uint64be _compareValue = compareValue;
|
||||
uint64be _swapValue = swapValue;
|
||||
bool r = mem->compare_exchange_strong(_compareValue, _swapValue);
|
||||
*previousValue = _compareValue;
|
||||
return r;
|
||||
}
|
||||
|
||||
void InitializeAtomic()
|
||||
{
|
||||
// 32bit atomic operations
|
||||
cafeExportRegister("coreinit", OSSwapAtomic, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSCompareAndSwapAtomic, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSCompareAndSwapAtomicEx, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSAddAtomic, LogType::Placeholder);
|
||||
|
||||
// 64bit atomic operations
|
||||
cafeExportRegister("coreinit", OSSetAtomic64, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSGetAtomic64, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSSwapAtomic64, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSAddAtomic64, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSAndAtomic64, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSOrAtomic64, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSCompareAndSwapAtomic64, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSCompareAndSwapAtomicEx64, LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
21
src/Cafe/OS/libs/coreinit/coreinit_Atomic.h
Normal file
21
src/Cafe/OS/libs/coreinit/coreinit_Atomic.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include <atomic>
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
uint32 OSSwapAtomic(std::atomic<uint32be>* mem, uint32 newValue);
|
||||
bool OSCompareAndSwapAtomic(std::atomic<uint32be>* mem, uint32 compareValue, uint32 swapValue);
|
||||
bool OSCompareAndSwapAtomicEx(std::atomic<uint32be>* mem, uint32 compareValue, uint32 swapValue, uint32be* previousValue);
|
||||
uint32 OSAddAtomic(std::atomic<uint32be>* mem, uint32 adder);
|
||||
|
||||
uint64 OSSwapAtomic64(std::atomic<uint64be>* mem, uint64 newValue);
|
||||
uint64 OSSetAtomic64(std::atomic<uint64be>* mem, uint64 newValue);
|
||||
uint64 OSGetAtomic64(std::atomic<uint64be>* mem);
|
||||
uint64 OSAddAtomic64(std::atomic<uint64be>* mem, uint64 adder);
|
||||
uint64 OSAndAtomic64(std::atomic<uint64be>* mem, uint64 val);
|
||||
uint64 OSOrAtomic64(std::atomic<uint64be>* mem, uint64 val);
|
||||
bool OSCompareAndSwapAtomic64(std::atomic<uint64be>* mem, uint64 compareValue, uint64 swapValue);
|
||||
bool OSCompareAndSwapAtomicEx64(std::atomic<uint64be>* mem, uint64 compareValue, uint64 swapValue, uint64be* previousValue);
|
||||
|
||||
void InitializeAtomic();
|
||||
}
|
||||
19
src/Cafe/OS/libs/coreinit/coreinit_BSP.cpp
Normal file
19
src/Cafe/OS/libs/coreinit/coreinit_BSP.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "coreinit_BSP.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
bool bspGetHardwareVersion(uint32be* version)
|
||||
{
|
||||
uint8 highVersion = 0x11; // anything below 0x11 will be considered as Hollywood
|
||||
// todo: Check version returned on console
|
||||
uint32 tempVers = highVersion << 24;
|
||||
*version = tempVers;
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitializeBSP()
|
||||
{
|
||||
cafeExportRegister("coreinit", bspGetHardwareVersion, LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
4
src/Cafe/OS/libs/coreinit/coreinit_BSP.h
Normal file
4
src/Cafe/OS/libs/coreinit/coreinit_BSP.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
namespace coreinit
|
||||
{
|
||||
void InitializeBSP();
|
||||
};
|
||||
118
src/Cafe/OS/libs/coreinit/coreinit_Callbacks.cpp
Normal file
118
src/Cafe/OS/libs/coreinit/coreinit_Callbacks.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#pragma once
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "util/helpers/fspinlock.h"
|
||||
|
||||
struct CoreinitAsyncCallback
|
||||
{
|
||||
CoreinitAsyncCallback(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10) :
|
||||
m_functionMPTR(functionMPTR), m_numParameters(numParameters), m_gprParam{ r3, r4, r5, r6, r7, r8, r9, r10 } {};
|
||||
|
||||
static void queue(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10)
|
||||
{
|
||||
s_asyncCallbackSpinlock.acquire();
|
||||
s_asyncCallbackQueue.emplace_back(allocateAndInitFromPool(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10));
|
||||
s_asyncCallbackSpinlock.release();
|
||||
}
|
||||
|
||||
static void callNextFromQueue()
|
||||
{
|
||||
s_asyncCallbackSpinlock.acquire();
|
||||
if (s_asyncCallbackQueue.empty())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "AsyncCallbackQueue is empty. Unexpected behavior");
|
||||
s_asyncCallbackSpinlock.release();
|
||||
return;
|
||||
}
|
||||
CoreinitAsyncCallback* cb = s_asyncCallbackQueue[0];
|
||||
s_asyncCallbackQueue.erase(s_asyncCallbackQueue.begin());
|
||||
s_asyncCallbackSpinlock.release();
|
||||
cb->doCall();
|
||||
s_asyncCallbackSpinlock.acquire();
|
||||
releaseToPool(cb);
|
||||
s_asyncCallbackSpinlock.release();
|
||||
}
|
||||
|
||||
private:
|
||||
void doCall()
|
||||
{
|
||||
PPCCoreCallback(m_functionMPTR, m_gprParam[0], m_gprParam[1], m_gprParam[2], m_gprParam[3], m_gprParam[4], m_gprParam[5], m_gprParam[6], m_gprParam[7]);
|
||||
}
|
||||
|
||||
static CoreinitAsyncCallback* allocateAndInitFromPool(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10)
|
||||
{
|
||||
cemu_assert_debug(s_asyncCallbackSpinlock.isHolding());
|
||||
if (s_asyncCallbackPool.empty())
|
||||
{
|
||||
CoreinitAsyncCallback* cb = new CoreinitAsyncCallback(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10);
|
||||
return cb;
|
||||
}
|
||||
CoreinitAsyncCallback* cb = s_asyncCallbackPool[0];
|
||||
s_asyncCallbackPool.erase(s_asyncCallbackPool.begin());
|
||||
|
||||
*cb = CoreinitAsyncCallback(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10);
|
||||
return cb;
|
||||
}
|
||||
|
||||
static void releaseToPool(CoreinitAsyncCallback* cb)
|
||||
{
|
||||
cemu_assert_debug(s_asyncCallbackSpinlock.isHolding());
|
||||
s_asyncCallbackPool.emplace_back(cb);
|
||||
}
|
||||
|
||||
static std::vector<struct CoreinitAsyncCallback*> s_asyncCallbackPool;
|
||||
static std::vector<struct CoreinitAsyncCallback*> s_asyncCallbackQueue;
|
||||
static FSpinlock s_asyncCallbackSpinlock;
|
||||
|
||||
sint32 m_numParameters;
|
||||
uint32 m_gprParam[9];
|
||||
MPTR m_functionMPTR;
|
||||
};
|
||||
|
||||
std::vector<struct CoreinitAsyncCallback*> CoreinitAsyncCallback::s_asyncCallbackPool;
|
||||
std::vector<struct CoreinitAsyncCallback*> CoreinitAsyncCallback::s_asyncCallbackQueue;
|
||||
FSpinlock CoreinitAsyncCallback::s_asyncCallbackSpinlock;
|
||||
|
||||
SysAllocator<OSThread_t> g_coreinitCallbackThread;
|
||||
SysAllocator<uint8, 1024*64> _g_coreinitCallbackThreadStack;
|
||||
SysAllocator<coreinit::OSSemaphore> g_asyncCallbackAsync;
|
||||
SysAllocator<char, 32> _g_coreinitCBThreadName;
|
||||
|
||||
void _coreinitCallbackThread(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
while (coreinit::OSWaitSemaphore(g_asyncCallbackAsync.GetPtr()))
|
||||
{
|
||||
CoreinitAsyncCallback::callNextFromQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void coreinitAsyncCallback_addWithLock(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10)
|
||||
{
|
||||
cemu_assert_debug(numParameters <= 8);
|
||||
if (functionMPTR >= 0x10000000)
|
||||
{
|
||||
cemuLog_log(LogType::Force, fmt::format("Suspicious callback address {0:08x} params: {1:08x} {2:08x} {3:08x} {4:08x}", functionMPTR, r3, r4, r5, r6));
|
||||
cemuLog_waitForFlush();
|
||||
}
|
||||
CoreinitAsyncCallback::queue(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10);
|
||||
__OSLockScheduler();
|
||||
coreinit::OSSignalSemaphoreInternal(g_asyncCallbackAsync.GetPtr(), false);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void coreinitAsyncCallback_add(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock() == false); // do not call when holding scheduler lock
|
||||
coreinitAsyncCallback_addWithLock(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10);
|
||||
}
|
||||
|
||||
void InitializeAsyncCallback()
|
||||
{
|
||||
coreinit::OSInitSemaphore(g_asyncCallbackAsync.GetPtr(), 0);
|
||||
|
||||
coreinit::OSCreateThreadType(g_coreinitCallbackThread.GetPtr(), PPCInterpreter_makeCallableExportDepr(_coreinitCallbackThread), 0, nullptr, _g_coreinitCallbackThreadStack.GetPtr() + _g_coreinitCallbackThreadStack.GetByteSize(), (sint32)_g_coreinitCallbackThreadStack.GetByteSize(), 0, 7, OSThread_t::THREAD_TYPE::TYPE_IO);
|
||||
coreinit::OSResumeThread(g_coreinitCallbackThread.GetPtr());
|
||||
|
||||
strcpy(_g_coreinitCBThreadName.GetPtr(), "Callback Thread");
|
||||
coreinit::OSSetThreadName(g_coreinitCallbackThread.GetPtr(), _g_coreinitCBThreadName.GetPtr());
|
||||
}
|
||||
148
src/Cafe/OS/libs/coreinit/coreinit_CodeGen.cpp
Normal file
148
src/Cafe/OS/libs/coreinit/coreinit_CodeGen.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h"
|
||||
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
|
||||
#include "Common/ExceptionHandler/ExceptionHandler.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool rangeIsAllocated;
|
||||
MPTR rangeStart;
|
||||
uint32 rangeSize;
|
||||
uint8* cacheStateCopy; // holds a copy of the entire range, simulates icache state (updated via ICBI)
|
||||
}coreinitCodeGen = {0};
|
||||
|
||||
void codeGenArea_memoryWriteCallback(void* pageStart, size_t size)
|
||||
{
|
||||
uint32 ea = memory_getVirtualOffsetFromPointer(pageStart);
|
||||
uint32 eaEnd = ea + (uint32)size;
|
||||
while (ea <= eaEnd)
|
||||
{
|
||||
codeGenHandleICBI(ea);
|
||||
ea += 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
void OSGetCodegenVirtAddrRange(betype<uint32>* rangeStart, betype<uint32>* rangeSize)
|
||||
{
|
||||
uint32 codegenSize = 0x01000000; // todo: Read from cos.xml
|
||||
//debug_printf("OSGetCodegenVirtAddrRange(0x%08x,0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
// on first call, allocate range
|
||||
if( coreinitCodeGen.rangeIsAllocated == false )
|
||||
{
|
||||
coreinitCodeGen.rangeStart = RPLLoader_AllocateCodeSpace(codegenSize, 0x1000);
|
||||
coreinitCodeGen.rangeSize = codegenSize;
|
||||
coreinitCodeGen.cacheStateCopy = new uint8[codegenSize];
|
||||
memset(coreinitCodeGen.cacheStateCopy, 0, codegenSize);
|
||||
coreinitCodeGen.rangeIsAllocated = true;
|
||||
}
|
||||
*rangeStart = coreinitCodeGen.rangeStart;
|
||||
*rangeSize = coreinitCodeGen.rangeSize;
|
||||
}
|
||||
|
||||
void OSGetCodegenVirtAddrRangeInternal(uint32& rangeStart, uint32& rangeSize)
|
||||
{
|
||||
if (coreinitCodeGen.rangeIsAllocated == 0)
|
||||
{
|
||||
rangeStart = 0;
|
||||
rangeSize = 0;
|
||||
return;
|
||||
}
|
||||
rangeStart = coreinitCodeGen.rangeStart;
|
||||
rangeSize = coreinitCodeGen.rangeSize;
|
||||
}
|
||||
|
||||
void ICInvalidateRange(uint32 startAddress, uint32 size)
|
||||
{
|
||||
uint32 ea = startAddress & ~0x1F;
|
||||
uint32 eaEnd = (startAddress + size + 0x1F)&~0x1F;
|
||||
while (ea <= eaEnd)
|
||||
{
|
||||
codeGenHandleICBI(ea);
|
||||
ea += 0x20;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void coreinitExport_OSCodegenCopy(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
if( coreinitCodeGen.rangeIsAllocated == false )
|
||||
assert_dbg();
|
||||
|
||||
uint32 codeAddrDest = hCPU->gpr[3];
|
||||
uint32 memAddrSrc = hCPU->gpr[4];
|
||||
uint32 size = hCPU->gpr[5];
|
||||
|
||||
if( codeAddrDest < coreinitCodeGen.rangeStart || codeAddrDest >= (coreinitCodeGen.rangeStart+coreinitCodeGen.rangeSize) )
|
||||
assert_dbg();
|
||||
if( (codeAddrDest+size) < coreinitCodeGen.rangeStart || (codeAddrDest+size) > (coreinitCodeGen.rangeStart+coreinitCodeGen.rangeSize) )
|
||||
assert_dbg();
|
||||
|
||||
memcpy(memory_getPointerFromVirtualOffset(codeAddrDest), memory_getPointerFromVirtualOffset(memAddrSrc), size);
|
||||
|
||||
// invalidate recompiler range
|
||||
uint32 ea = codeAddrDest & ~0x1F;
|
||||
uint32 eaEnd = (codeAddrDest + size + 0x1F)&~0x1F;
|
||||
while (ea <= eaEnd)
|
||||
{
|
||||
codeGenHandleICBI(ea);
|
||||
ea += 0x20;
|
||||
}
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void codeGenHandleICBI(uint32 ea)
|
||||
{
|
||||
cemu_assert_debug((ea & 0x1F) == 0);
|
||||
if (coreinitCodeGen.rangeIsAllocated == false)
|
||||
return;
|
||||
cemu_assert_debug((coreinitCodeGen.rangeStart & 0x1F) == 0);
|
||||
cemu_assert_debug((coreinitCodeGen.rangeSize & 0x1F) == 0);
|
||||
if (ea >= coreinitCodeGen.rangeStart && ea < (coreinitCodeGen.rangeStart + coreinitCodeGen.rangeSize))
|
||||
{
|
||||
uint8* cacheCopy = coreinitCodeGen.cacheStateCopy + (ea - coreinitCodeGen.rangeStart);
|
||||
uint8* currentState = memory_getPointerFromVirtualOffset(ea);
|
||||
if (memcmp(currentState, cacheCopy, 32) != 0)
|
||||
{
|
||||
// instructions changed
|
||||
// flush cache
|
||||
PPCRecompiler_invalidateRange(ea, ea+0x20);
|
||||
// update icache copy
|
||||
memcpy(cacheCopy, currentState, 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _avoidCodeGenJIT = false;
|
||||
|
||||
// currently we dont handle code invalidation well for direct write access
|
||||
// therefore if we detect attempts to write we will disable JITing the area
|
||||
bool codeGenShouldAvoid()
|
||||
{
|
||||
return _avoidCodeGenJIT;
|
||||
}
|
||||
|
||||
bool OSSwitchSecCodeGenMode(bool isRXOnly)
|
||||
{
|
||||
if (!_avoidCodeGenJIT)
|
||||
{
|
||||
forceLog_printf("Disable JIT on dynamic code area");
|
||||
}
|
||||
_avoidCodeGenJIT = true; // this function getting called is usually a sign that
|
||||
// does this have a return value?
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitializeCodeGen()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSGetCodegenVirtAddrRange, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", ICInvalidateRange, LogType::Placeholder);
|
||||
|
||||
osLib_addFunction("coreinit", "OSCodegenCopy", coreinitExport_OSCodegenCopy);
|
||||
|
||||
cafeExportRegister("coreinit", OSSwitchSecCodeGenMode, LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
10
src/Cafe/OS/libs/coreinit/coreinit_CodeGen.h
Normal file
10
src/Cafe/OS/libs/coreinit/coreinit_CodeGen.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void OSGetCodegenVirtAddrRangeInternal(uint32& rangeStart, uint32& rangeSize);
|
||||
void codeGenHandleICBI(uint32 ea);
|
||||
bool codeGenShouldAvoid();
|
||||
|
||||
void InitializeCodeGen();
|
||||
}
|
||||
100
src/Cafe/OS/libs/coreinit/coreinit_Coroutine.cpp
Normal file
100
src/Cafe/OS/libs/coreinit/coreinit_Coroutine.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Coroutine.h"
|
||||
#include "Cafe/HW/Espresso/PPCState.h"
|
||||
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
|
||||
#include "Cafe/HW/MMU/MMU.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
void coreinitExport_OSInitCoroutine(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
OSCoroutine* coroutine = (OSCoroutine*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
coroutine->lr = _swapEndianU32(hCPU->gpr[4]);
|
||||
coroutine->r1 = _swapEndianU32(hCPU->gpr[5]);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitCoroutine_OSSaveCoroutine(OSCoroutine* coroutine, PPCInterpreter_t* hCPU)
|
||||
{
|
||||
coroutine->lr = _swapEndianU32(hCPU->spr.LR);
|
||||
coroutine->cr = _swapEndianU32(ppc_getCR(hCPU));
|
||||
coroutine->gqr1 = _swapEndianU32(hCPU->spr.UGQR[1]);
|
||||
coroutine->r1 = _swapEndianU32(hCPU->gpr[1]);
|
||||
coroutine->r2 = _swapEndianU32(hCPU->gpr[2]);
|
||||
coroutine->r13 = _swapEndianU32(hCPU->gpr[13]);
|
||||
for (sint32 i = 14; i < 32; i++)
|
||||
coroutine->gpr[i - 14] = _swapEndianU32(hCPU->gpr[i]);
|
||||
for (sint32 i = 14; i < 32; i++)
|
||||
{
|
||||
coroutine->fpr[i - 14] = _swapEndianU64(hCPU->fpr[i].fp0int);
|
||||
}
|
||||
for (sint32 i = 14; i < 32; i++)
|
||||
{
|
||||
coroutine->psr[i - 14] = _swapEndianU64(hCPU->fpr[i].fp1int);
|
||||
}
|
||||
}
|
||||
|
||||
void coreinitCoroutine_OSLoadCoroutine(OSCoroutine* coroutine, PPCInterpreter_t* hCPU)
|
||||
{
|
||||
hCPU->spr.LR = _swapEndianU32(coroutine->lr);
|
||||
ppc_setCR(hCPU, _swapEndianU32(coroutine->cr));
|
||||
hCPU->spr.UGQR[1] = _swapEndianU32(coroutine->gqr1);
|
||||
hCPU->gpr[1] = _swapEndianU32(coroutine->r1);
|
||||
hCPU->gpr[2] = _swapEndianU32(coroutine->r2);
|
||||
hCPU->gpr[13] = _swapEndianU32(coroutine->r13);
|
||||
for (sint32 i = 14; i < 32; i++)
|
||||
hCPU->gpr[i] = _swapEndianU32(coroutine->gpr[i - 14]);
|
||||
for (sint32 i = 14; i < 32; i++)
|
||||
{
|
||||
hCPU->fpr[i].fp0int = _swapEndianU64(coroutine->fpr[i - 14]);
|
||||
}
|
||||
for (sint32 i = 14; i < 32; i++)
|
||||
{
|
||||
hCPU->fpr[i].fp1int = _swapEndianU64(coroutine->psr[i - 14]);
|
||||
}
|
||||
}
|
||||
|
||||
void coreinitExport_OSSwitchCoroutine(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
OSCoroutine* coroutineCurrent = (OSCoroutine*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
OSCoroutine* coroutineNext = (OSCoroutine*)memory_getPointerFromVirtualOffsetAllowNull(hCPU->gpr[4]);
|
||||
coreinitCoroutine_OSSaveCoroutine(coroutineCurrent, hCPU);
|
||||
if (coroutineNext != NULL)
|
||||
{
|
||||
coreinitCoroutine_OSLoadCoroutine(coroutineNext, hCPU);
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_OSSwitchFiberEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(param0, 0);
|
||||
ppcDefineParamU32(param1, 1);
|
||||
ppcDefineParamU32(param2, 2);
|
||||
ppcDefineParamU32(param3, 3);
|
||||
ppcDefineParamMPTR(newInstructionPointer, 4);
|
||||
ppcDefineParamMPTR(newStackPointer, 5);
|
||||
|
||||
MPTR prevStackpointer = hCPU->gpr[1];
|
||||
hCPU->gpr[1] = newStackPointer;
|
||||
hCPU->gpr[3] = param0;
|
||||
hCPU->gpr[4] = param1;
|
||||
hCPU->gpr[5] = param2;
|
||||
hCPU->gpr[6] = param3;
|
||||
|
||||
PPCCore_executeCallbackInternal(newInstructionPointer);
|
||||
uint32 returnValue = hCPU->gpr[3];
|
||||
|
||||
hCPU->gpr[1] = prevStackpointer;
|
||||
|
||||
osLib_returnFromFunction(hCPU, returnValue);
|
||||
}
|
||||
|
||||
void InitializeCoroutine()
|
||||
{
|
||||
osLib_addFunction("coreinit", "OSInitCoroutine", coreinitExport_OSInitCoroutine);
|
||||
osLib_addFunction("coreinit", "OSSwitchCoroutine", coreinitExport_OSSwitchCoroutine);
|
||||
osLib_addFunction("coreinit", "OSSwitchFiberEx", coreinitExport_OSSwitchFiberEx);
|
||||
}
|
||||
}
|
||||
21
src/Cafe/OS/libs/coreinit/coreinit_Coroutine.h
Normal file
21
src/Cafe/OS/libs/coreinit/coreinit_Coroutine.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct OSCoroutine
|
||||
{
|
||||
/* +0x00 */ uint32 lr;
|
||||
/* +0x04 */ uint32 cr;
|
||||
/* +0x08 */ uint32 gqr1;
|
||||
/* +0x0C */ uint32 r1; // stack pointer
|
||||
/* +0x10 */ uint32 r2;
|
||||
/* +0x14 */ uint32 r13;
|
||||
/* +0x18 */ uint32 gpr[18];
|
||||
uint64 fpr[18];
|
||||
uint64 psr[18];
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSCoroutine) == 0x180);
|
||||
|
||||
void InitializeCoroutine();
|
||||
}
|
||||
147
src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp
Normal file
147
src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/HW/Espresso/PPCCallback.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
MPTR _osDynLoadFuncAlloc = MPTR_NULL;
|
||||
MPTR _osDynLoadFuncFree = MPTR_NULL;
|
||||
MPTR _osDynLoadTLSFuncAlloc = MPTR_NULL;
|
||||
MPTR _osDynLoadTLSFuncFree = MPTR_NULL;
|
||||
|
||||
uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc)
|
||||
{
|
||||
_osDynLoadFuncAlloc = allocFunc;
|
||||
_osDynLoadFuncFree = freeFunc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc)
|
||||
{
|
||||
_osDynLoadTLSFuncAlloc = allocFunc;
|
||||
_osDynLoadTLSFuncFree = freeFunc;
|
||||
}
|
||||
|
||||
uint32 OSDynLoad_GetAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree)
|
||||
{
|
||||
*funcAlloc = _osDynLoadFuncAlloc;
|
||||
*funcFree = _osDynLoadFuncFree;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OSDynLoad_GetTLSAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree)
|
||||
{
|
||||
*funcAlloc = _osDynLoadTLSFuncAlloc;
|
||||
*funcFree = _osDynLoadTLSFuncFree;
|
||||
}
|
||||
|
||||
void* OSDynLoad_AllocatorAlloc(sint32 size, sint32 alignment)
|
||||
{
|
||||
if (_osDynLoadFuncAlloc == MPTR_NULL)
|
||||
return MPTR_NULL;
|
||||
StackAllocator<MEMPTR<void>> _ptrStorage;
|
||||
int r = PPCCoreCallback(_osDynLoadFuncAlloc, size, alignment, _ptrStorage.GetMPTR());
|
||||
if (r != 0)
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
return MPTR_NULL;
|
||||
}
|
||||
return _ptrStorage->GetPtr();
|
||||
}
|
||||
|
||||
void OSDynLoad_AllocatorFree(void* mem)
|
||||
{
|
||||
if (_osDynLoadFuncFree == MPTR_NULL)
|
||||
return;
|
||||
MEMPTR<void> _mem = mem;
|
||||
PPCCoreCallback(_osDynLoadFuncFree, _mem);
|
||||
}
|
||||
|
||||
uint32 OSDynLoad_Acquire(const char* libName, uint32be* moduleHandleOut)
|
||||
{
|
||||
// truncate path
|
||||
sint32 fileNameStartIndex = 0;
|
||||
sint32 tempLen = (sint32)strlen(libName);
|
||||
for (sint32 i = tempLen - 1; i >= 0; i--)
|
||||
{
|
||||
if (libName[i] == '/')
|
||||
{
|
||||
fileNameStartIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// truncate file extension
|
||||
char tempLibName[512];
|
||||
strcpy(tempLibName, libName + fileNameStartIndex);
|
||||
tempLen = (sint32)strlen(tempLibName);
|
||||
for (sint32 i = tempLen - 1; i >= 0; i--)
|
||||
{
|
||||
if (tempLibName[i] == '.')
|
||||
{
|
||||
tempLibName[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
// search for loaded modules with matching name
|
||||
uint32 rplHandle = RPLLoader_GetHandleByModuleName(libName);
|
||||
|
||||
if (rplHandle == RPL_INVALID_HANDLE)
|
||||
{
|
||||
RPLLoader_AddDependency(libName);
|
||||
RPLLoader_UpdateDependencies();
|
||||
RPLLoader_Link();
|
||||
RPLLoader_CallEntrypoints();
|
||||
rplHandle = RPLLoader_GetHandleByModuleName(libName);
|
||||
if (rplHandle == RPL_INVALID_HANDLE)
|
||||
rplHandle = 0;
|
||||
}
|
||||
|
||||
*moduleHandleOut = rplHandle;
|
||||
// return module not found error code
|
||||
if (rplHandle == RPL_INVALID_HANDLE)
|
||||
return 0xFFFCFFE9;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 OSDynLoad_Release(uint32 moduleHandle)
|
||||
{
|
||||
if (moduleHandle == RPL_INVALID_HANDLE)
|
||||
return 0;
|
||||
RPLLoader_RemoveDependency(moduleHandle);
|
||||
RPLLoader_UpdateDependencies();
|
||||
|
||||
// this function isn't supposed to return anything, but early versions of Cemu did and Cemuhook (up to 0.5.7.6) now relies on it. We still keep the return value around for compatibility
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 OSDynLoad_FindExport(uint32 moduleHandle, uint32 isData, const char* exportName, betype<MPTR>* addrOut)
|
||||
{
|
||||
if (moduleHandle == 0xFFFFFFFF)
|
||||
{
|
||||
// main module
|
||||
// Assassins Creed 4 has this handle hardcoded
|
||||
moduleHandle = RPLLoader_GetMainModuleHandle();
|
||||
}
|
||||
|
||||
MPTR exportResult = RPLLoader_FindModuleOrHLEExport(moduleHandle, isData, exportName);
|
||||
*addrOut = exportResult;
|
||||
|
||||
if (exportResult == MPTR_NULL)
|
||||
return 0xFFFFFFFF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InitializeDynLoad()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSDynLoad_SetAllocator, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSDynLoad_SetTLSAllocator, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSDynLoad_GetAllocator, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSDynLoad_GetTLSAllocator, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", OSDynLoad_Acquire, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSDynLoad_Release, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSDynLoad_FindExport, LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
18
src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h
Normal file
18
src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc);
|
||||
void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc);
|
||||
uint32 OSDynLoad_GetAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree);
|
||||
void OSDynLoad_GetTLSAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree);
|
||||
|
||||
void* OSDynLoad_AllocatorAlloc(sint32 size, sint32 alignment);
|
||||
void OSDynLoad_AllocatorFree(void* mem);
|
||||
|
||||
uint32 OSDynLoad_Acquire(const char* libName, uint32be* moduleHandleOut);
|
||||
uint32 OSDynLoad_Release(uint32 moduleHandle);
|
||||
uint32 OSDynLoad_FindExport(uint32 moduleHandle, uint32 isData, const char* exportName, betype<MPTR>* addrOut);
|
||||
|
||||
void InitializeDynLoad();
|
||||
}
|
||||
200
src/Cafe/OS/libs/coreinit/coreinit_FG.cpp
Normal file
200
src/Cafe/OS/libs/coreinit/coreinit_FG.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include <memory>
|
||||
|
||||
#define FG_BUCKET_AREA_FREE 0 // free area available game
|
||||
#define FG_BUCKET_AREA_AUDIO_TRANSITION 1 // transition audio buffer
|
||||
#define FG_BUCKET_AREA2 2 // frame storage? TV?
|
||||
#define FG_BUCKET_AREA3 3 // frame storage? DRC?
|
||||
#define FG_BUCKET_AREA4 4 // frame storage? TV?
|
||||
#define FG_BUCKET_AREA5 5 // frame storage? DRC?
|
||||
#define FG_BUCKET_AREA_SAVE 6
|
||||
#define FG_BUCKET_AREA_COPY 7 // for OS copy data (clipboard and title switch parameters)
|
||||
|
||||
#define FG_BUCKET_AREA_FREE_SIZE 0x2800000
|
||||
#define FG_BUCKET_AREA_SAVE_SIZE 0x1000
|
||||
#define FG_BUCKET_AREA_COPY_SIZE 0x400000
|
||||
|
||||
#define FG_BUCKET_AREA_COUNT 8
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
MEMPTR<void> fgAddr = nullptr; // NULL if not in foreground
|
||||
MEMPTR<uint8> fgSaveAreaAddr = nullptr;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 id;
|
||||
uint32 startOffset;
|
||||
uint32 size;
|
||||
}fgAreaEntries[FG_BUCKET_AREA_COUNT] =
|
||||
{
|
||||
{ 0, 0, 0x2800000 },
|
||||
{ 7, 0x2800000, 0x400000 },
|
||||
{ 1, 0x2C00000, 0x900000 },
|
||||
{ 2, 0x3500000, 0x3C0000 },
|
||||
{ 3, 0x38C0000, 0x1C0000 },
|
||||
{ 4, 0x3A80000, 0x3C0000 },
|
||||
{ 5, 0x3E40000, 0x1BF000 },
|
||||
{ 6, 0x3FFF000, 0x1000 }
|
||||
};
|
||||
|
||||
MEMPTR<uint8> GetFGMemByArea(uint32 areaId)
|
||||
{
|
||||
if (fgAddr == nullptr)
|
||||
return nullptr;
|
||||
for (sint32 i = 0; i < FG_BUCKET_AREA_COUNT; i++)
|
||||
{
|
||||
if (fgAreaEntries[i].id == areaId)
|
||||
return MEMPTR<uint8>(fgAddr.GetPtr<uint8>() + fgAreaEntries[i].startOffset);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool OSGetForegroundBucket(MEMPTR<void>* offset, uint32be* size)
|
||||
{
|
||||
// return full size of foreground bucket area
|
||||
if (offset)
|
||||
*offset = MEMPTR<void>{ (uint32)MEMORY_FGBUCKET_AREA_ADDR };
|
||||
if (size)
|
||||
*size = MEMORY_FGBUCKET_AREA_SIZE;
|
||||
// return true if in foreground
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSGetForegroundBucketFreeArea(MPTR* offset, MPTR* size)
|
||||
{
|
||||
uint8* freeAreaAddr = GetFGMemByArea(FG_BUCKET_AREA_FREE).GetPtr();
|
||||
|
||||
*offset = _swapEndianU32(memory_getVirtualOffsetFromPointer(freeAreaAddr));
|
||||
*size = _swapEndianU32(FG_BUCKET_AREA_FREE_SIZE);
|
||||
// return true if in foreground
|
||||
return (fgAddr != nullptr);
|
||||
}
|
||||
|
||||
void coreinitExport_OSGetForegroundBucket(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
//debug_printf("OSGetForegroundBucket(0x%x,0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
// returns the whole FG bucket area (if the current process is in the foreground)
|
||||
ppcDefineParamMPTR(areaOutput, 0);
|
||||
ppcDefineParamMPTR(areaSize, 1);
|
||||
bool r = OSGetForegroundBucket((MEMPTR<void>*)memory_getPointerFromVirtualOffsetAllowNull(areaOutput), (uint32be*)memory_getPointerFromVirtualOffsetAllowNull(areaSize));
|
||||
osLib_returnFromFunction(hCPU, r ? 1 : 0);
|
||||
}
|
||||
|
||||
void coreinitExport_OSGetForegroundBucketFreeArea(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("OSGetForegroundBucketFreeArea(0x%x,0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
ppcDefineParamMPTR(areaOutput, 0);
|
||||
ppcDefineParamMPTR(areaSize, 1);
|
||||
bool r = OSGetForegroundBucketFreeArea((MPTR*)memory_getPointerFromVirtualOffsetAllowNull(areaOutput), (MPTR*)memory_getPointerFromVirtualOffsetAllowNull(areaSize));
|
||||
osLib_returnFromFunction(hCPU, r ? 1 : 0);
|
||||
}
|
||||
|
||||
void InitForegroundBucket()
|
||||
{
|
||||
uint32be fgSize;
|
||||
OSGetForegroundBucket(&fgAddr, &fgSize);
|
||||
uint8* freeAreaPtr = GetFGMemByArea(FG_BUCKET_AREA_FREE).GetPtr();
|
||||
memset(freeAreaPtr, 0, FG_BUCKET_AREA_FREE_SIZE);
|
||||
uint8* saveAreaPtr = GetFGMemByArea(FG_BUCKET_AREA_SAVE).GetPtr();
|
||||
fgSaveAreaAddr = saveAreaPtr;
|
||||
if (*(uint32be*)saveAreaPtr != (uint32be)'Save')
|
||||
{
|
||||
// clear save area
|
||||
memset(saveAreaPtr, 0, FG_BUCKET_AREA_SAVE_SIZE);
|
||||
// clear copy area
|
||||
memset(GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr(), 0, FG_BUCKET_AREA_COPY_SIZE);
|
||||
// init save area
|
||||
*(uint32be*)(saveAreaPtr + 0x00) = 'Save';
|
||||
*(uint32be*)(saveAreaPtr + 0x08) |= 0x300;
|
||||
}
|
||||
}
|
||||
|
||||
void __OSClearCopyData()
|
||||
{
|
||||
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
|
||||
if (fgCopyArea)
|
||||
{
|
||||
*(uint32be*)(fgCopyArea + 0x00) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 __OSGetCopyDataSize()
|
||||
{
|
||||
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
|
||||
if (fgCopyArea)
|
||||
{
|
||||
return *(uint32be*)(fgCopyArea + 0x00);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8* __OSGetCopyDataPtr()
|
||||
{
|
||||
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
|
||||
if (fgCopyArea)
|
||||
return fgCopyArea + 4;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool __OSAppendCopyData(uint8* data, sint32 length)
|
||||
{
|
||||
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
|
||||
if (fgCopyArea == nullptr)
|
||||
return false;
|
||||
uint32 currentOffset = *(uint32be*)(fgCopyArea + 0x00);
|
||||
if ((currentOffset + length) > FG_BUCKET_AREA_COPY_SIZE - 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
memcpy(fgCopyArea + currentOffset + 4, data, length);
|
||||
*(uint32be*)(fgCopyArea + 0x00) += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool __OSResizeCopyData(sint32 length)
|
||||
{
|
||||
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
|
||||
if (fgCopyArea == nullptr)
|
||||
return false;
|
||||
sint32 currentOffset = (sint32) * (uint32be*)(fgCopyArea + 0x00);
|
||||
if (length < currentOffset)
|
||||
{
|
||||
// can only make copy data smaller
|
||||
*(uint32be*)(fgCopyArea + 0x00) = length;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OSCopyFromClipboard(void* data, uint32be* size)
|
||||
{
|
||||
uint32 cSize = *size;
|
||||
if (cSize == 0 && data == nullptr)
|
||||
return false;
|
||||
if (OSGetForegroundBucket(nullptr, nullptr) == false)
|
||||
return false;
|
||||
|
||||
// todo
|
||||
|
||||
*size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void coreinitExport_OSCopyFromClipboard(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("OSCopyFromClipboard(0x%x,0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
ppcDefineParamMEMPTR(buffer, void, 0);
|
||||
ppcDefineParamMEMPTR(size, uint32be, 1);
|
||||
bool r = OSCopyFromClipboard(buffer.GetPtr(), size.GetPtr());
|
||||
osLib_returnFromFunction(hCPU, r ? 1 : 0);
|
||||
}
|
||||
|
||||
void InitializeFG()
|
||||
{
|
||||
osLib_addFunction("coreinit", "OSGetForegroundBucket", coreinitExport_OSGetForegroundBucket);
|
||||
osLib_addFunction("coreinit", "OSGetForegroundBucketFreeArea", coreinitExport_OSGetForegroundBucketFreeArea);
|
||||
osLib_addFunction("coreinit", "OSCopyFromClipboard", coreinitExport_OSCopyFromClipboard);
|
||||
}
|
||||
}
|
||||
17
src/Cafe/OS/libs/coreinit/coreinit_FG.h
Normal file
17
src/Cafe/OS/libs/coreinit/coreinit_FG.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void __OSClearCopyData();
|
||||
uint32 __OSGetCopyDataSize();
|
||||
uint8* __OSGetCopyDataPtr();
|
||||
bool __OSAppendCopyData(uint8* data, sint32 length);
|
||||
bool __OSResizeCopyData(sint32 length);
|
||||
|
||||
bool OSGetForegroundBucket(MEMPTR<void>* offset, uint32be* size);
|
||||
bool OSGetForegroundBucketFreeArea(MPTR* offset, MPTR* size);
|
||||
|
||||
void InitForegroundBucket();
|
||||
|
||||
void InitializeFG();
|
||||
}
|
||||
1662
src/Cafe/OS/libs/coreinit/coreinit_FS.cpp
Normal file
1662
src/Cafe/OS/libs/coreinit/coreinit_FS.cpp
Normal file
File diff suppressed because it is too large
Load diff
325
src/Cafe/OS/libs/coreinit/coreinit_FS.h
Normal file
325
src/Cafe/OS/libs/coreinit/coreinit_FS.h
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
#pragma once
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "Cafe/IOSU/iosu_ipc_common.h"
|
||||
#include "Cafe/IOSU/fsa/fsa_types.h"
|
||||
#include "Cafe/IOSU/fsa/iosu_fsa.h"
|
||||
#include "coreinit_MessageQueue.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32be fileHandle;
|
||||
}FSFileHandleDepr_t;
|
||||
|
||||
typedef MEMPTR<betype<FSDirHandle2>> FSDirHandlePtr;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MEMPTR<void> userCallback;
|
||||
MEMPTR<void> userContext;
|
||||
MEMPTR<coreinit::OSMessageQueue> ioMsgQueue;
|
||||
}FSAsyncParamsNew_t;
|
||||
|
||||
static_assert(sizeof(FSAsyncParamsNew_t) == 0xC);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MPTR userCallback; // 0x96C
|
||||
MPTR userContext;
|
||||
MPTR ioMsgQueue;
|
||||
}FSAsyncParams_t; // legacy struct. Replace with FSAsyncParamsNew_t
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct FSCmdQueue
|
||||
{
|
||||
enum class QUEUE_FLAG : uint32
|
||||
{
|
||||
IS_FULL = (1 << 0), // waiting for Ioctl(v) result
|
||||
CANCEL_ALL = (1 << 4),
|
||||
};
|
||||
|
||||
/* +0x00 */ MPTR firstMPTR;
|
||||
/* +0x04 */ MPTR lastMPTR;
|
||||
/* +0x08 */ OSMutex mutex;
|
||||
/* +0x34 */ MPTR dequeueHandlerFuncMPTR;
|
||||
/* +0x38 */ uint32be numCommandsInFlight;
|
||||
/* +0x3C */ uint32 numMaxCommandsInFlight;
|
||||
/* +0x40 */ betype<QUEUE_FLAG> queueFlags;
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(FSCmdQueue::QUEUE_FLAG);
|
||||
|
||||
#define FS_CLIENT_BUFFER_SIZE (5888)
|
||||
#define FS_CMD_BLOCK_SIZE (2688)
|
||||
|
||||
struct FSClient_t
|
||||
{
|
||||
uint8 buffer[FS_CLIENT_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
struct FSCmdBlock_t
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8 buffer[FS_CMD_BLOCK_SIZE];
|
||||
struct
|
||||
{
|
||||
uint32 mount_it;
|
||||
}data;
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(FSCmdBlock_t) == FS_CMD_BLOCK_SIZE);
|
||||
|
||||
struct FSClientBody_t
|
||||
{
|
||||
uint8 ukn0000[0x100];
|
||||
uint8 ukn0100[0x100];
|
||||
uint8 ukn0200[0x100];
|
||||
uint8 ukn0300[0x100];
|
||||
uint8 ukn0400[0x100];
|
||||
uint8 ukn0500[0x100];
|
||||
uint8 ukn0600[0x100];
|
||||
uint8 ukn0700[0x100];
|
||||
uint8 ukn0800[0x100];
|
||||
uint8 ukn0900[0x100];
|
||||
uint8 ukn0A00[0x100];
|
||||
uint8 ukn0B00[0x100];
|
||||
uint8 ukn0C00[0x100];
|
||||
uint8 ukn0D00[0x100];
|
||||
uint8 ukn0E00[0x100];
|
||||
uint8 ukn0F00[0x100];
|
||||
uint8 ukn1000[0x100];
|
||||
uint8 ukn1100[0x100];
|
||||
uint8 ukn1200[0x100];
|
||||
uint8 ukn1300[0x100];
|
||||
uint8 ukn1400[0x10];
|
||||
uint8 ukn1410[0x10];
|
||||
uint8 ukn1420[0x10];
|
||||
uint8 ukn1430[0x10];
|
||||
uint32 ukn1440;
|
||||
betype<IOSDevHandle> iosuFSAHandle;
|
||||
uint32 ukn1448;
|
||||
uint32 ukn144C;
|
||||
uint8 ukn1450[0x10];
|
||||
uint8 ukn1460[0x10];
|
||||
uint8 ukn1470[0x10];
|
||||
FSCmdQueue fsCmdQueue;
|
||||
/* +0x14C4 */ MEMPTR<struct FSCmdBlockBody_t> currentCmdBlockBody; // set to currently active cmd
|
||||
uint32 ukn14C8;
|
||||
uint32 ukn14CC;
|
||||
uint8 ukn14D0[0x10];
|
||||
uint8 ukn14E0[0x10];
|
||||
uint8 ukn14F0[0x10];
|
||||
uint8 ukn1500[0x100];
|
||||
uint32 ukn1600;
|
||||
uint32 ukn1604;
|
||||
uint32 ukn1608;
|
||||
uint32 ukn160C;
|
||||
uint32 ukn1610;
|
||||
MEMPTR<FSClientBody_t> fsClientBodyNext; // next FSClientBody_t* in list of registered clients (list is circular, the last element points to the first element)
|
||||
uint32 ukn1618;
|
||||
/* +0x161C */ MEMPTR<FSClient_t> selfClient; // pointer to FSClient struct which holds this FSClientBody
|
||||
uint32 ukn1620;
|
||||
};
|
||||
|
||||
struct FSAsyncResult
|
||||
{
|
||||
/* +0x00 */ FSAsyncParamsNew_t fsAsyncParamsNew;
|
||||
|
||||
// fs message storage
|
||||
struct FSMessage
|
||||
{
|
||||
/* +0x0C / 0x0978 */ MEMPTR<FSAsyncResult> fsAsyncResult;
|
||||
/* +0x10 */ MPTR fsClientMPTR2; // 0x097C
|
||||
/* +0x14 */ MPTR fsCmdBlockMPTR; // 0x0980
|
||||
/* +0x18 */ MPTR commandType; // 0x0984
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
OSMessage osMsg;
|
||||
FSMessage fsMsg;
|
||||
}msgUnion;
|
||||
|
||||
/* +0x1C */ MEMPTR<FSClient_t> fsClient; // 0x0988
|
||||
/* +0x20 */ MEMPTR<FSCmdBlock_t> fsCmdBlock; // 0x98C
|
||||
/* +0x24 */ uint32be fsStatusNew; // 0x990
|
||||
};
|
||||
|
||||
static_assert(sizeof(FSAsyncResult) == 0x28);
|
||||
|
||||
struct FSCmdBlockBody_t
|
||||
{
|
||||
iosu::fsa::FSAIpcCommand ipcData;
|
||||
uint8 ukn0820[0x10];
|
||||
uint8 ukn0830[0x10];
|
||||
uint8 ukn0840[0x10];
|
||||
uint8 ukn0850[0x10];
|
||||
uint8 ukn0860[0x10];
|
||||
uint8 ukn0870[0x10];
|
||||
MPTR fsCmdBlockBodyMPTR;
|
||||
uint32 ukn0884;
|
||||
uint32 ukn0888;
|
||||
uint32 destBuffer88CMPTR;
|
||||
uint32 ukn0890;
|
||||
uint32 ukn0894;
|
||||
uint32 ukn0898;
|
||||
uint32 ukn089C;
|
||||
uint32 ukn08A0;
|
||||
uint32 ukn08A4;
|
||||
uint32 ukn08A8;
|
||||
uint32 ukn08AC;
|
||||
uint8 ukn08B0[0x10];
|
||||
uint8 ukn08C0[0x10];
|
||||
uint8 ukn08D0[0x10];
|
||||
uint8 ukn08E0[0x10];
|
||||
uint8 ukn08F0[0x10];
|
||||
/* +0x0900 */ uint32be operationType;
|
||||
betype<IOSDevHandle> fsaDevHandle;
|
||||
/* +0x0908 */ uint16be ipcReqType; // 0 -> IoctlAsync, 1 -> IoctlvAsync
|
||||
uint8 ukn090A;
|
||||
uint8 ukn090B;
|
||||
uint32 ukn090C;
|
||||
uint32 ukn0910;
|
||||
uint32 ukn0914;
|
||||
uint32 ukn0918;
|
||||
uint32 ukn091C;
|
||||
uint32 ukn0920;
|
||||
uint32 ukn0924;
|
||||
uint32 ukn0928;
|
||||
uint32 ukn092C;
|
||||
uint32 ukn0930;
|
||||
uint32 ukn0934;
|
||||
/* +0x0938 */ MEMPTR<FSClientBody_t> fsClientBody;
|
||||
/* +0x093C */ uint32 statusCode; // not a status code but rather the state? Uses weird values for some reason
|
||||
/* +0x0940 */ uint32be cancelState; // bitmask. Bit 0 -> If set command has been canceled
|
||||
// return values
|
||||
/* +0x0944 */ uint32 returnValueMPTR; // returnedFilePos (used to store pointer to variable that holds return value?), also used by QUERYINFO to store pointer for result. Also used for GetCwd() to hold the pointer for the returned dir path. Also used by OPENFILE to hold returned fileHandle
|
||||
/* +0x0948 */ uint32 transferSize; // number of bytes to transfer
|
||||
// transfer control?
|
||||
uint32 uknVal094C;
|
||||
uint32 transferElemSize; // number of bytes of a single transferred element (count of elements can be calculated via count = transferSize/transferElemSize)
|
||||
uint32 uknVal0954; // this is set to max(0x10, transferSize) for reads and to min(0x40000, transferSize) for writes?
|
||||
// link for cmd queue
|
||||
MPTR nextMPTR; // points towards FSCmdQueue->first
|
||||
MPTR previousMPTR; // points towards FSCmdQueue->last
|
||||
|
||||
/* +0x960 */ betype<FSA_RESULT> lastFSAStatus;
|
||||
uint32 ukn0964;
|
||||
/* +0x0968 */ uint8 errHandling; // return error flag mask
|
||||
/* +0x096C */ FSAsyncResult asyncResult;
|
||||
/* +0x0994 */ MEMPTR<void> userData;
|
||||
/* +0x0998 */ OSMessageQueue syncTaskMsgQueue; // this message queue is used when mapping asynchronous tasks to synchronous API
|
||||
/* +0x09D4 */ OSMessage _syncTaskMsg[1];
|
||||
/* +0x09E4 */ MPTR cmdFinishFuncMPTR;
|
||||
/* +0x09E8 */ uint8 priority;
|
||||
uint8 uknStatusGuessed09E9;
|
||||
uint8 ukn09EA;
|
||||
uint8 ukn09EB;
|
||||
uint32 ukn09EC;
|
||||
uint32 ukn9F0;
|
||||
uint32be ukn9F4_lastErrorRelated;
|
||||
/* +0x9F8 */ MEMPTR<FSCmdBlock_t> selfCmdBlock;
|
||||
uint32 ukn9FC;
|
||||
};
|
||||
|
||||
static_assert(sizeof(FSAsyncParams_t) == 0xC);
|
||||
static_assert(sizeof(FSCmdBlock_t) == 0xA80);
|
||||
|
||||
#define FSA_CMD_FLAG_SET_POS (1<<0)
|
||||
|
||||
#define FSA_CMD_OPERATION_TYPE_CHANGEDIR (0x5)
|
||||
#define FSA_CMD_OPERATION_TYPE_GETCWD (0x6)
|
||||
#define FSA_CMD_OPERATION_TYPE_MAKEDIR (0x7)
|
||||
#define FSA_CMD_OPERATION_TYPE_REMOVE (0x8)
|
||||
#define FSA_CMD_OPERATION_TYPE_RENAME (0x9)
|
||||
#define FSA_CMD_OPERATION_TYPE_OPENDIR (0xA)
|
||||
#define FSA_CMD_OPERATION_TYPE_READDIR (0xB)
|
||||
#define FSA_CMD_OPERATION_TYPE_CLOSEDIR (0xD)
|
||||
#define FSA_CMD_OPERATION_TYPE_OPENFILE (0xE)
|
||||
#define FSA_CMD_OPERATION_TYPE_READ (0xF)
|
||||
#define FSA_CMD_OPERATION_TYPE_WRITE (0x10)
|
||||
#define FSA_CMD_OPERATION_TYPE_GETPOS (0x11)
|
||||
#define FSA_CMD_OPERATION_TYPE_SETPOS (0x12)
|
||||
#define FSA_CMD_OPERATION_TYPE_ISEOF (0x13)
|
||||
#define FSA_CMD_OPERATION_TYPE_GETSTATFILE (0x14)
|
||||
#define FSA_CMD_OPERATION_TYPE_CLOSEFILE (0x15)
|
||||
#define FSA_CMD_OPERATION_TYPE_QUERYINFO (0x18)
|
||||
#define FSA_CMD_OPERATION_TYPE_APPENDFILE (0x19)
|
||||
#define FSA_CMD_OPERATION_TYPE_TRUNCATEFILE (0x1A)
|
||||
#define FSA_CMD_OPERATION_TYPE_FLUSHQUOTA (0x1E)
|
||||
|
||||
|
||||
#define FSA_CMD_STATUS_CODE_D900A21 0xD900A21 // cmd block is initialized
|
||||
#define FSA_CMD_STATUS_CODE_D900A22 0xD900A22 // cmd block is queued
|
||||
#define FSA_CMD_STATUS_CODE_D900A24 0xD900A24 // cmd block was processed and is available again
|
||||
#define FSA_CMD_STATUS_CODE_D900A26 0xD900A26 // cmd block result is being processed
|
||||
|
||||
enum FS_VOLSTATE : sint32
|
||||
{
|
||||
FS_VOLSTATE_READY = 1,
|
||||
};
|
||||
|
||||
// internal interface
|
||||
sint32 __FSQueryInfoAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* queryString, uint32 queryType, void* queryResult, uint32 errHandling, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
|
||||
// coreinit exports
|
||||
FS_RESULT FSAddClientEx(FSClient_t* fsClient, uint32 uknR4, uint32 errHandling);
|
||||
FS_RESULT FSAddClient(FSClient_t* fsClient, uint32 errHandling);
|
||||
FS_RESULT FSDelClient(FSClient_t* fsClient, uint32 errHandling);
|
||||
|
||||
void FSInitCmdBlock(FSCmdBlock_t* fsCmdBlock);
|
||||
|
||||
sint32 FSOpenFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, char* mode, FSFileHandleDepr_t* fileHandle, uint32 errHandling, FSAsyncParamsNew_t* asyncParams);
|
||||
sint32 FSOpenFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, char* mode, FSFileHandleDepr_t* fileHandle, uint32 errHandling);
|
||||
|
||||
sint32 FSReadFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSReadFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask);
|
||||
sint32 FSReadFileWithPosAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSReadFileWithPos(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask);
|
||||
|
||||
sint32 FSWriteFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSWriteFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask);
|
||||
sint32 FSWriteFileWithPosAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSWriteFileWithPos(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask);
|
||||
|
||||
sint32 FSSetPosFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 filePos, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSSetPosFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 filePos, uint32 errorMask);
|
||||
sint32 FSGetPosFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32be* returnedFilePos, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSGetPosFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32be* returnedFilePos, uint32 errorMask);
|
||||
|
||||
sint32 FSAppendFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 size, uint32 count, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSAppendFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 size, uint32 count, uint32 errorMask);
|
||||
|
||||
sint32 FSIsEofAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSIsEof(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errorMask);
|
||||
|
||||
sint32 FSRenameAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* srcPath, char* dstPath, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSRename(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* srcPath, char* dstPath, uint32 errorMask);
|
||||
sint32 FSRemoveAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* filePath, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSRemove(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* filePath, uint32 errorMask);
|
||||
sint32 FSMakeDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const uint8* dirPath, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSMakeDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const uint8* path, uint32 errorMask);
|
||||
sint32 FSChangeDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSChangeDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask);
|
||||
sint32 FSGetCwdAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* dirPathOut, sint32 dirPathMaxLen, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSGetCwd(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* dirPathOut, sint32 dirPathMaxLen, uint32 errorMask);
|
||||
|
||||
sint32 FSGetFreeSpaceSizeAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, FSLargeSize* returnedFreeSize, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSGetFreeSpaceSize(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, FSLargeSize* returnedFreeSize, uint32 errorMask);
|
||||
|
||||
sint32 FSOpenDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, FSDirHandlePtr dirHandleOut, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSOpenDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, FSDirHandlePtr dirHandleOut, uint32 errorMask);
|
||||
sint32 FSReadDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, FSDirEntry_t* dirEntryOut, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSReadDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, FSDirEntry_t* dirEntryOut, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSCloseDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSCloseDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask);
|
||||
|
||||
sint32 FSFlushQuotaAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
|
||||
sint32 FSFlushQuota(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask);
|
||||
|
||||
FS_VOLSTATE FSGetVolumeState(FSClient_t* fsClient);
|
||||
|
||||
void InitializeFS();
|
||||
};
|
||||
|
||||
285
src/Cafe/OS/libs/coreinit/coreinit_GHS.cpp
Normal file
285
src/Cafe/OS/libs/coreinit/coreinit_GHS.cpp
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_GHS.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct iobbuf
|
||||
{
|
||||
uint32be ukn00; // lock index?
|
||||
uint32be ukn04; // ?
|
||||
uint32be ukn08; // ?
|
||||
uint32be flags; // permissions and channel
|
||||
};
|
||||
|
||||
#define GHS_FOPEN_MAX 100
|
||||
|
||||
struct GHSAccessibleData
|
||||
{
|
||||
iobbuf _iob[GHS_FOPEN_MAX];
|
||||
MPTR _iob_lock[GHS_FOPEN_MAX];
|
||||
uint16be __gh_FOPEN_MAX;
|
||||
MEMPTR<void> ghs_environ;
|
||||
uint32 ghs_Errno; // exposed by __gh_errno_ptr() or via 'errno' data export
|
||||
};
|
||||
|
||||
SysAllocator<GHSAccessibleData> g_ghs_data;
|
||||
|
||||
struct ghs_flock
|
||||
{
|
||||
uint32be mutexIndex;
|
||||
};
|
||||
|
||||
void __ghs_flock_create(ghs_flock* flock);
|
||||
ghs_flock* __ghs_flock_ptr(iobbuf* iob);
|
||||
|
||||
std::recursive_mutex g_ghsLock;
|
||||
std::recursive_mutex g_ghsLockFlock;
|
||||
|
||||
SysAllocator<coreinit::OSMutex, GHS_FOPEN_MAX> _flockMutexArray;
|
||||
bool _flockMutexMask[GHS_FOPEN_MAX]; // if set, mutex in _flockMutexArray is reserved
|
||||
|
||||
#define IOB_FLAG_IN (0x1)
|
||||
#define IOB_FLAG_OUT (0x2)
|
||||
|
||||
#define IOB_FLAG_CHANNEL(__x) ((__x)<<18)
|
||||
|
||||
void PrepareGHSRuntime()
|
||||
{
|
||||
g_ghs_data->ghs_environ = nullptr;
|
||||
g_ghs_data->__gh_FOPEN_MAX = GHS_FOPEN_MAX;
|
||||
g_ghs_data->ghs_Errno = 0;
|
||||
|
||||
for (sint32 i = 0; i < GHS_FOPEN_MAX; i++)
|
||||
_flockMutexMask[i] = false;
|
||||
// init stdin/stdout/stderr
|
||||
g_ghs_data->_iob[0].flags = IOB_FLAG_IN;
|
||||
g_ghs_data->_iob[1].flags = IOB_FLAG_OUT;
|
||||
g_ghs_data->_iob[1].flags = IOB_FLAG_OUT;
|
||||
g_ghs_data->_iob[0].flags |= IOB_FLAG_CHANNEL(0);
|
||||
g_ghs_data->_iob[1].flags |= IOB_FLAG_CHANNEL(1);
|
||||
g_ghs_data->_iob[2].flags |= IOB_FLAG_CHANNEL(2);
|
||||
__ghs_flock_create(__ghs_flock_ptr(g_ghs_data->_iob + 0));
|
||||
__ghs_flock_create(__ghs_flock_ptr(g_ghs_data->_iob + 1));
|
||||
__ghs_flock_create(__ghs_flock_ptr(g_ghs_data->_iob + 2));
|
||||
|
||||
osLib_addVirtualPointer("coreinit", "__gh_FOPEN_MAX", memory_getVirtualOffsetFromPointer(&g_ghs_data->__gh_FOPEN_MAX));
|
||||
osLib_addVirtualPointer("coreinit", "_iob", memory_getVirtualOffsetFromPointer(g_ghs_data->_iob));
|
||||
osLib_addVirtualPointer("coreinit", "environ", memory_getVirtualOffsetFromPointer(&g_ghs_data->ghs_environ));
|
||||
osLib_addVirtualPointer("coreinit", "errno", memory_getVirtualOffsetFromPointer(&g_ghs_data->ghs_Errno));
|
||||
}
|
||||
|
||||
void __ghs_flock_create(ghs_flock* flock)
|
||||
{
|
||||
g_ghsLockFlock.lock();
|
||||
// find available mutex
|
||||
sint32 mutexIndex = -1;
|
||||
for (sint32 i = 0; i < GHS_FOPEN_MAX; i++)
|
||||
{
|
||||
if (!_flockMutexMask[i])
|
||||
{
|
||||
mutexIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mutexIndex == -1)
|
||||
{
|
||||
forceLog_printf("__ghs_flock_create(): No flock available");
|
||||
cemu_assert(false); // no available mutex
|
||||
}
|
||||
// mark mutex as reserved
|
||||
_flockMutexMask[mutexIndex] = true;
|
||||
// init mutex
|
||||
coreinit::OSInitMutexEx(_flockMutexArray.GetPtr() + mutexIndex, NULL);
|
||||
// update flock to point to the reserved mutex
|
||||
flock->mutexIndex = mutexIndex;
|
||||
g_ghsLockFlock.unlock();
|
||||
}
|
||||
|
||||
void __ghs_flock_destroy(uint32 index)
|
||||
{
|
||||
g_ghsLockFlock.lock();
|
||||
cemu_assert_debug(index > 2); // stdin/stdout/stderr should never be released?
|
||||
cemu_assert(index < GHS_FOPEN_MAX);
|
||||
cemu_assert_debug(_flockMutexMask[index]);
|
||||
_flockMutexMask[index] = false;
|
||||
g_ghsLockFlock.unlock();
|
||||
}
|
||||
|
||||
ghs_flock* __ghs_flock_ptr(iobbuf* iob)
|
||||
{
|
||||
size_t streamIndex = iob - g_ghs_data->_iob;
|
||||
return (ghs_flock*)&(g_ghs_data->_iob_lock[streamIndex]);
|
||||
}
|
||||
|
||||
void __ghs_flock_file(uint32 index)
|
||||
{
|
||||
cemu_assert(index < GHS_FOPEN_MAX);
|
||||
OSLockMutex(_flockMutexArray.GetPtr() + index);
|
||||
}
|
||||
|
||||
void __ghs_funlock_file(uint32 index)
|
||||
{
|
||||
cemu_assert(index < GHS_FOPEN_MAX);
|
||||
OSUnlockMutex(_flockMutexArray.GetPtr() + index);
|
||||
}
|
||||
|
||||
void __ghsLock()
|
||||
{
|
||||
while (!g_ghsLock.try_lock())
|
||||
{
|
||||
PPCCore_switchToScheduler();
|
||||
}
|
||||
}
|
||||
|
||||
void __ghsUnlock()
|
||||
{
|
||||
g_ghsLock.unlock();
|
||||
}
|
||||
|
||||
void* __get_eh_init_block()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* __get_eh_globals()
|
||||
{
|
||||
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
|
||||
return currentThread->crt.eh_globals.GetPtr();
|
||||
}
|
||||
|
||||
void* __get_eh_mem_manage()
|
||||
{
|
||||
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
|
||||
return ¤tThread->crt.eh_mem_manage;
|
||||
}
|
||||
|
||||
void* __gh_errno_ptr()
|
||||
{
|
||||
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
|
||||
return ¤tThread->context.error;
|
||||
}
|
||||
|
||||
void* __get_eh_store_globals()
|
||||
{
|
||||
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
|
||||
return ¤tThread->crt.eh_store_globals;
|
||||
}
|
||||
|
||||
void* __get_eh_store_globals_tdeh()
|
||||
{
|
||||
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
|
||||
return ¤tThread->crt.eh_store_globals_tdeh;
|
||||
}
|
||||
|
||||
struct ghs_mtx_t
|
||||
{
|
||||
MEMPTR<coreinit::OSMutex> mutexPtr;
|
||||
};
|
||||
|
||||
void __ghs_mtx_init(ghs_mtx_t* mtx)
|
||||
{
|
||||
mtx->mutexPtr = (coreinit::OSMutex*)coreinit::_weak_MEMAllocFromDefaultHeapEx(ppcsizeof<coreinit::OSMutex>(), 8);
|
||||
coreinit::OSInitMutex(mtx->mutexPtr.GetPtr());
|
||||
}
|
||||
|
||||
void __ghs_mtx_dst(ghs_mtx_t* mtx)
|
||||
{
|
||||
coreinit::_weak_MEMFreeToDefaultHeap(mtx->mutexPtr.GetPtr());
|
||||
mtx->mutexPtr = nullptr;
|
||||
}
|
||||
|
||||
void __ghs_mtx_lock(ghs_mtx_t* mtx)
|
||||
{
|
||||
coreinit::OSLockMutex(mtx->mutexPtr.GetPtr());
|
||||
}
|
||||
|
||||
void __ghs_mtx_unlock(ghs_mtx_t* mtx)
|
||||
{
|
||||
coreinit::OSUnlockMutex(mtx->mutexPtr.GetPtr());
|
||||
}
|
||||
|
||||
struct OSTLSBlock
|
||||
{
|
||||
MPTR addr;
|
||||
uint32 ukn04;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSTLSBlock) == 8);
|
||||
|
||||
struct TLS_Index
|
||||
{
|
||||
uint16 ukn00;
|
||||
uint16 tlsModuleIndex;
|
||||
MPTR ukn04;
|
||||
};
|
||||
|
||||
void* __tls_get_addr(TLS_Index* tlsIndex)
|
||||
{
|
||||
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
|
||||
|
||||
if (_swapEndianU16(tlsIndex->tlsModuleIndex) == 0)
|
||||
assert_dbg();
|
||||
|
||||
// check if we need to allocate additional TLS blocks for this thread
|
||||
if (_swapEndianU16(tlsIndex->tlsModuleIndex) >= _swapEndianU32(currentThread->numAllocatedTLSBlocks))
|
||||
{
|
||||
uint32 allocSize = (RPLLoader_GetMaxTLSModuleIndex() + 1) * sizeof(OSTLSBlock); // __OSDynLoad_gTLSHeader.ukn00 * 8;
|
||||
MPTR allocMem = coreinit_allocFromSysArea(allocSize, 4);
|
||||
memset(memory_getPointerFromVirtualOffset(allocMem), 0, allocSize);
|
||||
if (_swapEndianU32(currentThread->numAllocatedTLSBlocks) != 0)
|
||||
{
|
||||
// keep previously allocated blocks
|
||||
memcpy(memory_getPointerFromVirtualOffset(allocMem), memory_getPointerFromVirtualOffset(_swapEndianU32(currentThread->tlsBlocksMPTR)), _swapEndianU32(currentThread->numAllocatedTLSBlocks) * 8);
|
||||
}
|
||||
currentThread->tlsBlocksMPTR = _swapEndianU32(allocMem);
|
||||
currentThread->numAllocatedTLSBlocks = _swapEndianU16(RPLLoader_GetMaxTLSModuleIndex() + 1);
|
||||
}
|
||||
// look up TLS address based on moduleIndex
|
||||
OSTLSBlock* tlsBlock = (OSTLSBlock*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(currentThread->tlsBlocksMPTR) + sizeof(OSTLSBlock) * (uint32)_swapEndianU16(tlsIndex->tlsModuleIndex));
|
||||
if (tlsBlock->addr != _swapEndianU32(MPTR_NULL))
|
||||
{
|
||||
//osLib_returnFromFunction(hCPU, _swapEndianU32(tlsBlock->addr)+_swapEndianU32(tlsIndex->ukn04));
|
||||
return memory_getPointerFromVirtualOffset(_swapEndianU32(tlsBlock->addr) + _swapEndianU32(tlsIndex->ukn04));
|
||||
}
|
||||
// alloc data for TLS block
|
||||
uint8* tlsSectionData = nullptr;
|
||||
sint32 tlsSize = 0;
|
||||
|
||||
bool r = RPLLoader_GetTLSDataByTLSIndex((sint16)_swapEndianU16(tlsIndex->tlsModuleIndex), &tlsSectionData, &tlsSize);
|
||||
cemu_assert(r);
|
||||
cemu_assert(tlsSize != 0);
|
||||
|
||||
MPTR tlsData = coreinit_allocFromSysArea(tlsSize, 32);
|
||||
memcpy(memory_getPointerFromVirtualOffset(tlsData), tlsSectionData, tlsSize);
|
||||
tlsBlock->addr = _swapEndianU32(tlsData);
|
||||
return memory_getPointerFromVirtualOffset(_swapEndianU32(tlsBlock->addr) + _swapEndianU32(tlsIndex->ukn04));
|
||||
}
|
||||
|
||||
void InitializeGHS()
|
||||
{
|
||||
cafeExportRegister("coreinit", __ghs_flock_create, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghs_flock_destroy, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghs_flock_ptr, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghs_flock_file, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghs_funlock_file, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghsLock, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghsUnlock, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", __get_eh_init_block, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __get_eh_globals, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __get_eh_mem_manage, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __gh_errno_ptr, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __get_eh_store_globals, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __get_eh_store_globals_tdeh, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", __ghs_mtx_init, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghs_mtx_dst, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghs_mtx_lock, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __ghs_mtx_unlock, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", __tls_get_addr, LogType::Placeholder);
|
||||
}
|
||||
};
|
||||
8
src/Cafe/OS/libs/coreinit/coreinit_GHS.h
Normal file
8
src/Cafe/OS/libs/coreinit/coreinit_GHS.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void PrepareGHSRuntime();
|
||||
|
||||
void InitializeGHS();
|
||||
};
|
||||
138
src/Cafe/OS/libs/coreinit/coreinit_HWInterface.cpp
Normal file
138
src/Cafe/OS/libs/coreinit/coreinit_HWInterface.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "coreinit_HWInterface.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
enum class RegisterInterfaceId : uint32 // for __OSRead/__OSWrite API (register access in userspace)
|
||||
{
|
||||
INTERFACE_VI_UKN = 0, // 0x0C1E0000
|
||||
|
||||
INTERFACE_VI2_UKN = 3, // might also be some other interface?
|
||||
|
||||
|
||||
};
|
||||
|
||||
enum class SysRegisterInterfaceId : uint32 // for __OSRead/__OSWriteRegister (register access via kernel systemcall)
|
||||
{
|
||||
INTERFACE_UKN = 0,
|
||||
|
||||
INTERFACE_3_ACR_VI = 3, // 0x0D00021C
|
||||
|
||||
INTERFACE_6_SI = 6, // 0x0D006400
|
||||
INTERFACE_7_AI_PROBABLY = 7, // 0x0D046C00 // AI or some secondary AI interface?
|
||||
|
||||
};
|
||||
|
||||
PAddr _GetRegisterPhysicalAddress(RegisterInterfaceId interfaceId, uint32 offset)
|
||||
{
|
||||
PAddr base = 0;
|
||||
switch (interfaceId)
|
||||
{
|
||||
case RegisterInterfaceId::INTERFACE_VI_UKN:
|
||||
base = 0x0C1E0000;
|
||||
break;
|
||||
default:
|
||||
cemu_assert_debug(false); // todo
|
||||
return 0;
|
||||
}
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
|
||||
PAddr _GetSysRegisterPhysicalAddress(SysRegisterInterfaceId interfaceId, uint32 offset)
|
||||
{
|
||||
PAddr base = 0;
|
||||
switch (interfaceId)
|
||||
{
|
||||
case SysRegisterInterfaceId::INTERFACE_3_ACR_VI:
|
||||
base = 0x0D00021C;
|
||||
break;
|
||||
case SysRegisterInterfaceId::INTERFACE_6_SI:
|
||||
base = 0x0D006400;
|
||||
break;
|
||||
default:
|
||||
cemu_assert_debug(false); // todo
|
||||
return 0;
|
||||
}
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
/* Userspace register interface */
|
||||
|
||||
uint32 OSReadRegister32(RegisterInterfaceId interfaceId, uint32 offset)
|
||||
{
|
||||
PAddr regAddr = _GetRegisterPhysicalAddress(interfaceId, offset);
|
||||
cemu_assert_debug(regAddr);
|
||||
return MMU::ReadMMIO_32(regAddr);
|
||||
}
|
||||
|
||||
uint16 OSReadRegister16(RegisterInterfaceId interfaceId, uint32 offset)
|
||||
{
|
||||
PAddr regAddr = _GetRegisterPhysicalAddress(interfaceId, offset);
|
||||
cemu_assert_debug(regAddr);
|
||||
return MMU::ReadMMIO_16(regAddr);
|
||||
}
|
||||
|
||||
void OSWriteRegister16(uint16 newValue, RegisterInterfaceId interfaceId, uint32 offset)
|
||||
{
|
||||
static bool s_dbg = false;
|
||||
if (!s_dbg)
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
s_dbg = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OSWriteRegister32(uint16 newValue, RegisterInterfaceId interfaceId, uint32 offset)
|
||||
{
|
||||
static bool s_dbg = false;
|
||||
if (!s_dbg)
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
s_dbg = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OSModifyRegister16(RegisterInterfaceId interfaceId, uint32 uknR4, uint32 uknR5, uint32 uknR6)
|
||||
{
|
||||
static bool s_dbg = false;
|
||||
if (!s_dbg)
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
s_dbg = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Kernel register interface */
|
||||
|
||||
uint32 __OSReadRegister32Ex(SysRegisterInterfaceId interfaceId, uint32 registerId)
|
||||
{
|
||||
uint32 offset = registerId * 4;
|
||||
cemu_assert_debug(offset < 0x40);
|
||||
PAddr regAddr = _GetSysRegisterPhysicalAddress(interfaceId, offset);
|
||||
cemu_assert_debug(regAddr);
|
||||
return MMU::ReadMMIO_32(regAddr);
|
||||
}
|
||||
|
||||
void __OSWriteRegister32Ex(SysRegisterInterfaceId interfaceId, uint32 registerId, uint32 newValue)
|
||||
{
|
||||
uint32 offset = registerId * 4;
|
||||
cemu_assert_debug(offset < 0x40);
|
||||
PAddr regAddr = _GetSysRegisterPhysicalAddress(interfaceId, offset);
|
||||
cemu_assert_debug(regAddr);
|
||||
MMU::WriteMMIO_32(regAddr, newValue);
|
||||
}
|
||||
|
||||
void InitializeHWInterface()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSReadRegister32, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSReadRegister16, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSWriteRegister16, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSWriteRegister32, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSModifyRegister16, LogType::Placeholder);
|
||||
|
||||
|
||||
cafeExportRegister("coreinit", __OSReadRegister32Ex, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __OSWriteRegister32Ex, LogType::Placeholder);
|
||||
};
|
||||
};
|
||||
4
src/Cafe/OS/libs/coreinit/coreinit_HWInterface.h
Normal file
4
src/Cafe/OS/libs/coreinit/coreinit_HWInterface.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
namespace coreinit
|
||||
{
|
||||
void InitializeHWInterface();
|
||||
};
|
||||
137
src/Cafe/OS/libs/coreinit/coreinit_IM.cpp
Normal file
137
src/Cafe/OS/libs/coreinit/coreinit_IM.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
|
||||
// APD = Automatic Power Down
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
#define IM_ERROR_NONE 0
|
||||
|
||||
void coreinitExport_IMIsAPDEnabledBySysSettings(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("IMIsAPDEnabledBySysSettings(0x%08x)\n", hCPU->gpr[3]);
|
||||
ppcDefineParamTypePtr(isAPDEnabled, uint32be, 0);
|
||||
*isAPDEnabled = 0;
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
void coreinitExport_IMGetTimeBeforeAPD(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 uint32* returns the remaining number of seconds until auto-shutdown
|
||||
memory_writeU32(hCPU->gpr[3], 60 * 30); // 30 minutes
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
void coreinitExport_IMGetTimeBeforeDimming(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 uint32* returns the remaining number of seconds until dimming
|
||||
memory_writeU32(hCPU->gpr[3], 60 * 30); // 30 minutes
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
bool imDimIsEnabled = true;
|
||||
|
||||
void coreinitExport_IMEnableDim(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
imDimIsEnabled = true;
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
void coreinitExport_IMIsDimEnabled(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 uint32* returns the remaining number of seconds until auto-shutdown
|
||||
memory_writeU32(hCPU->gpr[3], imDimIsEnabled ? 1 : 0); // enabled
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
void coreinitExport_IMGetAPDPeriod(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("IMGetAPDPeriod(0x%08x)\n", hCPU->gpr[3]);
|
||||
// parameters:
|
||||
// r3 uint32* returns the number of seconds until auto-shutdown occurs
|
||||
memory_writeU32(hCPU->gpr[3], 600);
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
void coreinitExport_IM_GetParameter(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("IM_GetParameter()");
|
||||
|
||||
ppcDefineParamS32(imHandle, 0); // handle from IM_Open()
|
||||
ppcDefineParamS32(uknR4, 1);
|
||||
ppcDefineParamS32(parameterId, 2);
|
||||
ppcDefineParamStructPtr(output, void, 3);
|
||||
ppcDefineParamS32(uknR7, 4);
|
||||
ppcDefineParamS32(uknR8, 5);
|
||||
|
||||
if (parameterId == 0)
|
||||
{
|
||||
// inactive seconds
|
||||
*(uint32be*)output = 600;
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
void coreinitExport_IM_GetRuntimeParameter(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("IM_GetRuntimeParameter()");
|
||||
|
||||
ppcDefineParamS32(parameterId, 0);
|
||||
ppcDefineParamStructPtr(output, void, 1);
|
||||
|
||||
if (parameterId == 8)
|
||||
{
|
||||
// indicates if last session was ended due to auto-power-down
|
||||
*(uint32be*)output = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
void coreinitExport_IM_GetHomeButtonParams(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("IM_GetHomeButtonParams(...)\n");
|
||||
ppcDefineParamS32(imObj, 0);
|
||||
ppcDefineParamMPTR(ipcBuf, 1);
|
||||
ppcDefineParamMPTR(paramOut, 2);
|
||||
ppcDefineParamS32(uknR6, 3);
|
||||
ppcDefineParamS32(uknR7, 4);
|
||||
|
||||
// todo
|
||||
// note: No idea what these values mean. But they were chosen so that the Browser (surf.rpx) does not OSPanic()
|
||||
memory_writeU32(paramOut + 0x0, 0);
|
||||
memory_writeU32(paramOut + 0x4, 0);
|
||||
|
||||
// for scope.rpx (Download Manager)
|
||||
//memory_writeU32(paramOut + 0x0, 1);
|
||||
//memory_writeU32(paramOut + 0x4, 2); // some sort of index (starting at 1?)
|
||||
|
||||
|
||||
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
|
||||
}
|
||||
|
||||
void InitializeIM()
|
||||
{
|
||||
osLib_addFunction("coreinit", "IMIsAPDEnabledBySysSettings", coreinitExport_IMIsAPDEnabledBySysSettings);
|
||||
osLib_addFunction("coreinit", "IMGetTimeBeforeAPD", coreinitExport_IMGetTimeBeforeAPD);
|
||||
osLib_addFunction("coreinit", "IMGetTimeBeforeDimming", coreinitExport_IMGetTimeBeforeDimming);
|
||||
osLib_addFunction("coreinit", "IMEnableDim", coreinitExport_IMEnableDim);
|
||||
osLib_addFunction("coreinit", "IMIsDimEnabled", coreinitExport_IMIsDimEnabled);
|
||||
osLib_addFunction("coreinit", "IMGetAPDPeriod", coreinitExport_IMGetAPDPeriod);
|
||||
osLib_addFunction("coreinit", "IM_GetHomeButtonParams", coreinitExport_IM_GetHomeButtonParams);
|
||||
osLib_addFunction("coreinit", "IM_GetParameter", coreinitExport_IM_GetParameter);
|
||||
osLib_addFunction("coreinit", "IM_GetRuntimeParameter", coreinitExport_IM_GetRuntimeParameter);
|
||||
}
|
||||
|
||||
};
|
||||
6
src/Cafe/OS/libs/coreinit/coreinit_IM.h
Normal file
6
src/Cafe/OS/libs/coreinit/coreinit_IM.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitializeIM();
|
||||
};
|
||||
91
src/Cafe/OS/libs/coreinit/coreinit_IOS.cpp
Normal file
91
src/Cafe/OS/libs/coreinit/coreinit_IOS.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#include "Cafe/OS/libs/coreinit/coreinit_IOS.h"
|
||||
#include "Cafe/IOSU/legacy/iosu_ioctl.h"
|
||||
|
||||
// superseded by coreinit_IPC.cpp/h
|
||||
|
||||
sint32 __depr__IOS_Open(char* path, uint32 mode)
|
||||
{
|
||||
sint32 iosDevice = 0;
|
||||
if (path == nullptr)
|
||||
{
|
||||
iosDevice = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(path, IOS_PATH_ODM) == 0)
|
||||
iosDevice = IOS_DEVICE_ODM;
|
||||
else if (strcmp(path, IOS_PATH_SOCKET) == 0)
|
||||
iosDevice = IOS_DEVICE_SOCKET;
|
||||
else if (strcmp(path, IOS_PATH_ACT) == 0)
|
||||
iosDevice = IOS_DEVICE_ACT;
|
||||
else if (strcmp(path, IOS_PATH_FPD) == 0)
|
||||
iosDevice = IOS_DEVICE_FPD;
|
||||
else if (strcmp(path, IOS_PATH_ACP_MAIN) == 0)
|
||||
iosDevice = IOS_DEVICE_ACP_MAIN;
|
||||
else if (strcmp(path, IOS_PATH_MCP) == 0)
|
||||
iosDevice = IOS_DEVICE_MCP;
|
||||
else if (strcmp(path, IOS_PATH_BOSS) == 0)
|
||||
iosDevice = IOS_DEVICE_BOSS;
|
||||
else if (strcmp(path, IOS_PATH_NIM) == 0)
|
||||
iosDevice = IOS_DEVICE_NIM;
|
||||
else if (strcmp(path, IOS_PATH_IOSUHAX) == 0)
|
||||
return -1;
|
||||
else
|
||||
iosDevice = IOS_DEVICE_UKN;
|
||||
}
|
||||
return iosDevice;
|
||||
}
|
||||
|
||||
sint32 __depr__IOS_Ioctl(uint32 fd, uint32 request, void* inBuffer, uint32 inSize, void* outBuffer, uint32 outSize)
|
||||
{
|
||||
switch (fd)
|
||||
{
|
||||
case IOS_DEVICE_ODM:
|
||||
{
|
||||
// Home Menu uses ioctl cmd 5 on startup and then repeats cmd 4 every frame
|
||||
if (request == 4)
|
||||
{
|
||||
// check drive state
|
||||
debug_printf("checkDriveState()\n");
|
||||
*(uint32be*)outBuffer = 0xA;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf("odm unsupported ioctl %d\n", request);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// todo
|
||||
forceLogDebug_printf("Unsupported Ioctl command");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sint32 __depr__IOS_Ioctlv(uint32 fd, uint32 request, uint32 countIn, uint32 countOut, ioBufferVector_t* ioBufferVectors)
|
||||
{
|
||||
StackAllocator<ioQueueEntry_t> _queueEntryBuf;
|
||||
ioQueueEntry_t* queueEntry = _queueEntryBuf.GetPointer();
|
||||
|
||||
queueEntry->isIoctlv = true;
|
||||
queueEntry->isAsync = false;
|
||||
queueEntry->request = request;
|
||||
queueEntry->countIn = countIn;
|
||||
queueEntry->countOut = countOut;
|
||||
queueEntry->bufferVectors = ioBufferVectors;
|
||||
|
||||
queueEntry->ppcThread = nullptr;
|
||||
queueEntry->returnValue = 0;
|
||||
queueEntry->isCompleted = false;
|
||||
|
||||
sint32 r = iosuIoctl_pushAndWait(fd, queueEntry);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
sint32 __depr__IOS_Close(uint32 fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
9
src/Cafe/OS/libs/coreinit/coreinit_IOS.h
Normal file
9
src/Cafe/OS/libs/coreinit/coreinit_IOS.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// IOS
|
||||
typedef struct _ioBufferVector_t ioBufferVector_t;
|
||||
|
||||
sint32 __depr__IOS_Open(char* path, uint32 mode);
|
||||
sint32 __depr__IOS_Ioctl(uint32 fd, uint32 request, void* inBuffer, uint32 inSize, void* outBuffer, uint32 outSize);
|
||||
sint32 __depr__IOS_Ioctlv(uint32 fd, uint32 request, uint32 countIn, uint32 countOut, ioBufferVector_t* ioBufferVectors);
|
||||
sint32 __depr__IOS_Close(uint32 fd);
|
||||
|
||||
// superseded by coreinit_IPC.h
|
||||
482
src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp
Normal file
482
src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/IOSU/kernel/iosu_kernel.h"
|
||||
#include "Cafe/HW/Espresso/Const.h"
|
||||
#include "Cafe/HW/Espresso/PPCCallback.h"
|
||||
#include "coreinit_MessageQueue.h"
|
||||
#include "coreinit_IPC.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
static constexpr inline size_t IPC_NUM_RESOURCE_BUFFERS = 0x30;
|
||||
|
||||
struct IPCResourceBuffer
|
||||
{
|
||||
IPCCommandBody commandBody;
|
||||
uint8 bufferData[0x80 - 0x48];
|
||||
};
|
||||
|
||||
static_assert(sizeof(IPCCommandBody) == 0x48);
|
||||
static_assert(sizeof(IPCResourceBuffer) == 0x80);
|
||||
|
||||
struct IPCResourceBufferDescriptor
|
||||
{
|
||||
/* +0x00 */ uint32be IsAllocated;
|
||||
/* +0x04 */ MEMPTR<OSMessageQueue> asyncMsgQueue; // optional, if set a message will be sent to this queue...
|
||||
/* +0x08 */ MEMPTR<void> asyncResultFunc; // ...otherwise this is checked and delegated to the IPC threads. If false, only eventSynchronousIPC will be signaled. If true, a message will be sent to the per-core ipc queue
|
||||
/* +0x0C */ MEMPTR<void> asyncResultUserParam;
|
||||
/* +0x10 */ uint32 ukn10;
|
||||
/* +0x14 */ MEMPTR<IPCResourceBuffer> resourcePtr;
|
||||
/* +0x18 */ OSEvent eventSynchronousIPC;
|
||||
};
|
||||
|
||||
static_assert(sizeof(IPCResourceBufferDescriptor) == 0x3C);
|
||||
|
||||
struct IPCBufferFIFO
|
||||
{
|
||||
sint32be writeIndex;
|
||||
sint32be readIndex;
|
||||
sint32be numQueuedEntries;
|
||||
sint32be mostQueuedEntries;
|
||||
MEMPTR<IPCResourceBufferDescriptor> ringbufferArray[IPC_NUM_RESOURCE_BUFFERS];
|
||||
|
||||
void Init()
|
||||
{
|
||||
writeIndex = 0;
|
||||
readIndex = -1;
|
||||
numQueuedEntries = 0;
|
||||
mostQueuedEntries = 0;
|
||||
for (size_t i = 0; i < IPC_NUM_RESOURCE_BUFFERS; i++)
|
||||
ringbufferArray[i] = nullptr;
|
||||
}
|
||||
|
||||
void Push(IPCResourceBufferDescriptor* descriptor)
|
||||
{
|
||||
cemu_assert(readIndex != writeIndex); // if equal, fifo is full (should not happen as there not more buffers than ringbuffer entries)
|
||||
ringbufferArray[writeIndex] = descriptor;
|
||||
if (readIndex < 0)
|
||||
readIndex = writeIndex;
|
||||
writeIndex = (writeIndex + 1) % IPC_NUM_RESOURCE_BUFFERS;
|
||||
++numQueuedEntries;
|
||||
if (numQueuedEntries > mostQueuedEntries)
|
||||
mostQueuedEntries = numQueuedEntries;
|
||||
}
|
||||
|
||||
IPCResourceBufferDescriptor* Pop()
|
||||
{
|
||||
if (numQueuedEntries == 0)
|
||||
return nullptr;
|
||||
IPCResourceBufferDescriptor* r = ringbufferArray[readIndex].GetPtr();
|
||||
--numQueuedEntries;
|
||||
if (numQueuedEntries == 0)
|
||||
readIndex = -1;
|
||||
else
|
||||
readIndex = (readIndex + 1) % IPC_NUM_RESOURCE_BUFFERS;
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
struct IPCDriverCOSKernelCommunicationArea
|
||||
{
|
||||
uint32be numAvailableResponses;
|
||||
MEMPTR<IPCCommandBody> responseArray[11];
|
||||
};
|
||||
|
||||
static_assert(sizeof(IPCDriverCOSKernelCommunicationArea) == 0x30);
|
||||
|
||||
struct alignas(64) IPCDriver
|
||||
{
|
||||
betype<IPCDriverState> state;
|
||||
uint32 ukn04;
|
||||
uint32 coreIndex;
|
||||
uint32 writeIndexCmd020;
|
||||
MEMPTR<IPCResourceBuffer> resourceBuffers;
|
||||
|
||||
/* 0x16C */ IPCBufferFIFO fifoFreeBuffers;
|
||||
/* 0x23C */ IPCBufferFIFO fifoBuffersInFlight;
|
||||
|
||||
/* 0x334 */ uint32 resourceBuffersInitialized;
|
||||
/* 0x338 */ IPCDriverCOSKernelCommunicationArea kernelSharedArea; // this is passed to system call 0x1E00 (IPCOpen)
|
||||
|
||||
/* 0x3FC */ IPCResourceBufferDescriptor resBufferDescriptor[IPC_NUM_RESOURCE_BUFFERS];
|
||||
};
|
||||
|
||||
//static_assert(sizeof(IPCDriverInstance) == 0x1740);
|
||||
|
||||
SysAllocator<IPCResourceBuffer, IPC_NUM_RESOURCE_BUFFERS * Espresso::CORE_COUNT, 0x40> s_ipcResourceBuffers;
|
||||
SysAllocator<IPCDriver, Espresso::CORE_COUNT, 0x40> s_ipcDriver;
|
||||
|
||||
IPCDriver& IPCDriver_GetByCore(uint32 coreIndex)
|
||||
{
|
||||
cemu_assert_debug(coreIndex >= 0 && coreIndex < (uint32)Espresso::CORE_COUNT);
|
||||
return s_ipcDriver[coreIndex];
|
||||
}
|
||||
|
||||
void IPCDriver_InitForCore(uint32 coreIndex)
|
||||
{
|
||||
IPCDriver& ipcDriver = IPCDriver_GetByCore(coreIndex);
|
||||
ipcDriver.coreIndex = coreIndex;
|
||||
ipcDriver.state = IPCDriverState::INITIALIZED;
|
||||
ipcDriver.resourceBuffers = s_ipcResourceBuffers.GetPtr() + IPC_NUM_RESOURCE_BUFFERS * coreIndex;
|
||||
ipcDriver.resourceBuffersInitialized = 0;
|
||||
// setup resource descriptors
|
||||
for (size_t i = 0; i < IPC_NUM_RESOURCE_BUFFERS; i++)
|
||||
{
|
||||
ipcDriver.resBufferDescriptor[i].resourcePtr = ipcDriver.resourceBuffers.GetPtr() + i;
|
||||
ipcDriver.resBufferDescriptor[i].asyncResultFunc = nullptr;
|
||||
ipcDriver.resBufferDescriptor[i].asyncResultUserParam = nullptr;
|
||||
}
|
||||
ipcDriver.resourceBuffersInitialized = 1;
|
||||
// setup resource buffer FIFOs
|
||||
ipcDriver.fifoFreeBuffers.Init();
|
||||
ipcDriver.fifoBuffersInFlight.Init();
|
||||
for (size_t i = 0; i < IPC_NUM_RESOURCE_BUFFERS; i++)
|
||||
ipcDriver.fifoFreeBuffers.Push(ipcDriver.resBufferDescriptor + i);
|
||||
}
|
||||
|
||||
IPCResourceBufferDescriptor* IPCDriver_AllocateResource(IPCDriver* ipcDriver, IOSDevHandle devHandle, IPCCommandId cmdId, OSMessageQueue* asyncMessageQueue, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam)
|
||||
{
|
||||
cemu_assert_debug(ipcDriver->coreIndex == OSGetCoreId());
|
||||
IPCResourceBufferDescriptor* descriptor = nullptr;
|
||||
while (true)
|
||||
{
|
||||
descriptor = ipcDriver->fifoFreeBuffers.Pop();
|
||||
if (!descriptor)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IPCDriver: Exceeded free resources");
|
||||
OSYieldThread();
|
||||
cemu_assert_unimplemented(); // we should wait for an event instead of busylooping
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
cemu_assert_debug(descriptor >= ipcDriver->resBufferDescriptor && descriptor < ipcDriver->resBufferDescriptor + IPC_NUM_RESOURCE_BUFFERS);
|
||||
cemu_assert_debug(descriptor->resourcePtr.GetPtr() >= ipcDriver->resourceBuffers.GetPtr() && descriptor->resourcePtr.GetPtr() < (ipcDriver->resourceBuffers.GetPtr() + IPC_NUM_RESOURCE_BUFFERS));
|
||||
IPCResourceBuffer* res = descriptor->resourcePtr;
|
||||
IPCCommandBody& cmdBody = res->commandBody;
|
||||
|
||||
descriptor->IsAllocated = 1;
|
||||
descriptor->asyncMsgQueue = asyncMessageQueue;
|
||||
descriptor->asyncResultFunc = asyncResultFunc;
|
||||
descriptor->asyncResultUserParam = asyncResultUserParam;
|
||||
|
||||
cmdBody.cmdId = cmdId;
|
||||
cmdBody.ukn0C = 0;
|
||||
cmdBody.ukn14 = 0;
|
||||
cmdBody.result = 0;
|
||||
cmdBody.devHandle = devHandle;
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
void IPCDriver_ReleaseResource(IPCDriver* ipcDriver, IPCResourceBufferDescriptor* requestDescriptor)
|
||||
{
|
||||
requestDescriptor->IsAllocated = 0;
|
||||
ipcDriver->fifoFreeBuffers.Push(requestDescriptor);
|
||||
}
|
||||
|
||||
/* IPC threads */
|
||||
|
||||
SysAllocator<OSThread_t, Espresso::CORE_COUNT> gIPCThread;
|
||||
SysAllocator<uint8, 0x4000 * Espresso::CORE_COUNT> _gIPCThreadStack;
|
||||
SysAllocator<uint8, 0x18 * Espresso::CORE_COUNT> _gIPCThreadNameStorage;
|
||||
SysAllocator<OSMessageQueue, Espresso::CORE_COUNT> gIPCThreadMsgQueue;
|
||||
SysAllocator<OSMessage, Espresso::CORE_COUNT * IPC_NUM_RESOURCE_BUFFERS> _gIPCThreadSemaphoreStorage;
|
||||
|
||||
// handler thread for asynchronous callbacks for IPC responses
|
||||
void __IPCDriverThreadFunc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint32 coreIndex = OSGetCoreId();
|
||||
while (true)
|
||||
{
|
||||
OSMessage msg;
|
||||
OSReceiveMessage(gIPCThreadMsgQueue.GetPtr() + coreIndex, &msg, OS_MESSAGE_BLOCK);
|
||||
cemu_assert(msg.data2 == 1); // type must be callback
|
||||
MEMPTR<void> cbFunc{ msg.message };
|
||||
cemu_assert(cbFunc != nullptr);
|
||||
PPCCoreCallback(cbFunc.GetPtr(), (uint32)msg.data0, (uint32)msg.data1);
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void IPCDriver_InitIPCThread(uint32 coreIndex)
|
||||
{
|
||||
// create a thread with 0x4000 stack space
|
||||
// and a message queue large enough to hold the maximum number of commands (IPC_NUM_RESOURCE_BUFFERS)
|
||||
OSInitMessageQueue(gIPCThreadMsgQueue.GetPtr() + coreIndex, _gIPCThreadSemaphoreStorage.GetPtr() + coreIndex * IPC_NUM_RESOURCE_BUFFERS, IPC_NUM_RESOURCE_BUFFERS);
|
||||
OSThread_t* ipcThread = gIPCThread.GetPtr() + coreIndex;
|
||||
OSCreateThreadType(ipcThread, PPCInterpreter_makeCallableExportDepr(__IPCDriverThreadFunc), 0, nullptr, _gIPCThreadStack.GetPtr() + 0x4000 * coreIndex + 0x4000, 0x4000, 15, (1 << coreIndex), OSThread_t::THREAD_TYPE::TYPE_DRIVER);
|
||||
sprintf((char*)_gIPCThreadNameStorage.GetPtr()+coreIndex*0x18, "{SYS IPC Core %d}", coreIndex);
|
||||
OSSetThreadName(ipcThread, (char*)_gIPCThreadNameStorage.GetPtr() + coreIndex * 0x18);
|
||||
OSResumeThread(ipcThread);
|
||||
}
|
||||
|
||||
/* coreinit IOS_* API */
|
||||
|
||||
void _IPCDriver_SubmitCmdAllQueued(IPCDriver& ipcDriver)
|
||||
{
|
||||
// on COS, submitted commands first go to the COS kernel via syscall 0x2000, where they are processed, copied and queued again
|
||||
// we skip all of this and just pass our IPC commands directly to the IOSU kernel handler HLE function
|
||||
// important: IOSU needs to know which PPC core sent the command, so that it can also notify the same core about the result
|
||||
ipcDriver.state = IPCDriverState::SUBMITTING;
|
||||
while (true)
|
||||
{
|
||||
IPCResourceBufferDescriptor* res = ipcDriver.fifoBuffersInFlight.Pop();
|
||||
if (!res)
|
||||
break;
|
||||
// resolve pointers
|
||||
switch (res->resourcePtr->commandBody.cmdId)
|
||||
{
|
||||
case IPCCommandId::IOS_OPEN:
|
||||
res->resourcePtr->commandBody.args[0] = res->resourcePtr->commandBody.ppcVirt0.GetMPTR();
|
||||
break;
|
||||
case IPCCommandId::IOS_CLOSE:
|
||||
break;
|
||||
case IPCCommandId::IOS_IOCTL:
|
||||
res->resourcePtr->commandBody.args[1] = res->resourcePtr->commandBody.ppcVirt0.GetMPTR();
|
||||
res->resourcePtr->commandBody.args[3] = res->resourcePtr->commandBody.ppcVirt1.GetMPTR();
|
||||
break;
|
||||
case IPCCommandId::IOS_IOCTLV:
|
||||
res->resourcePtr->commandBody.args[3] = res->resourcePtr->commandBody.ppcVirt0.GetMPTR();
|
||||
break;
|
||||
default:
|
||||
cemu_assert_unimplemented();
|
||||
break;
|
||||
}
|
||||
iosu::kernel::IPCSubmitFromCOS(ipcDriver.coreIndex, &res->resourcePtr->commandBody);
|
||||
}
|
||||
ipcDriver.state = IPCDriverState::READY;
|
||||
}
|
||||
|
||||
void _IPCDriver_SubmitCmd(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor)
|
||||
{
|
||||
if (requestDescriptor->asyncResultFunc == nullptr)
|
||||
OSInitEvent(&requestDescriptor->eventSynchronousIPC, OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_AUTO);
|
||||
ipcDriver.fifoBuffersInFlight.Push(requestDescriptor);
|
||||
cemu_assert_debug(ipcDriver.state == IPCDriverState::READY || ipcDriver.state == IPCDriverState::INITIALIZED);
|
||||
_IPCDriver_SubmitCmdAllQueued(ipcDriver);
|
||||
}
|
||||
|
||||
uint32 _IPCDriver_WaitForResultAndRelease(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor)
|
||||
{
|
||||
OSWaitEvent(&requestDescriptor->eventSynchronousIPC);
|
||||
uint32 r = requestDescriptor->resourcePtr->commandBody.result;
|
||||
IPCDriver_ReleaseResource(&ipcDriver, requestDescriptor);
|
||||
return r;
|
||||
}
|
||||
|
||||
void IPCDriver_HandleResponse(IPCDriver& ipcDriver, IPCCommandBody* res, uint32 ppcCoreIndex)
|
||||
{
|
||||
size_t index = (IPCResourceBuffer*)res - ipcDriver.resourceBuffers.GetPtr();
|
||||
cemu_assert(index < IPC_NUM_RESOURCE_BUFFERS);
|
||||
IPCResourceBufferDescriptor* descriptor = ipcDriver.resBufferDescriptor + index;
|
||||
cemu_assert(descriptor->IsAllocated != 0);
|
||||
if (descriptor->asyncMsgQueue != nullptr)
|
||||
{
|
||||
OSMessage msg;
|
||||
msg.message = 0;
|
||||
msg.data0 = res->result;
|
||||
msg.data1 = descriptor->asyncResultUserParam.GetMPTR();
|
||||
msg.data2 = 0;
|
||||
sint32 r = OSSendMessage(descriptor->asyncMsgQueue.GetPtr(), &msg, 0);
|
||||
cemu_assert(r != 0);
|
||||
IPCDriver_ReleaseResource(&ipcDriver, descriptor);
|
||||
return;
|
||||
}
|
||||
if (descriptor->asyncResultFunc != nullptr)
|
||||
{
|
||||
OSMessage msg;
|
||||
msg.message = descriptor->asyncResultFunc.GetMPTR();
|
||||
msg.data0 = res->result;
|
||||
msg.data1 = descriptor->asyncResultUserParam.GetMPTR();
|
||||
msg.data2 = 1;
|
||||
sint32 r = OSSendMessage(gIPCThreadMsgQueue.GetPtr() + ppcCoreIndex, &msg, 0);
|
||||
cemu_assert(r != 0);
|
||||
IPCDriver_ReleaseResource(&ipcDriver, descriptor);
|
||||
return;
|
||||
}
|
||||
// signal event for synchronous IPC
|
||||
OSSignalEvent(&descriptor->eventSynchronousIPC);
|
||||
}
|
||||
|
||||
// handles responses queued in shared region
|
||||
void IPCDriver_KernelCallback(IPCDriver& ipcDriver)
|
||||
{
|
||||
cemu_assert_debug(ipcDriver.kernelSharedArea.numAvailableResponses != 0);
|
||||
for (uint32 i = 0; i < ipcDriver.kernelSharedArea.numAvailableResponses; i++)
|
||||
IPCDriver_HandleResponse(ipcDriver, ipcDriver.kernelSharedArea.responseArray[i], ipcDriver.coreIndex);
|
||||
ipcDriver.kernelSharedArea.numAvailableResponses = 0;
|
||||
}
|
||||
|
||||
// called by our HLE'd IOSU directly
|
||||
void IPCDriver_NotifyResponses(uint32 ppcCoreIndex, IPCCommandBody** responseArray, uint32 numResponses)
|
||||
{
|
||||
cemu_assert(numResponses < 11);
|
||||
IPCDriver& ipcDriver = IPCDriver_GetByCore(ppcCoreIndex);
|
||||
ipcDriver.kernelSharedArea.numAvailableResponses = numResponses;
|
||||
for (uint32 i = 0; i < numResponses; i++)
|
||||
ipcDriver.kernelSharedArea.responseArray[i] = responseArray[i];
|
||||
IPCDriver_KernelCallback(ipcDriver);
|
||||
}
|
||||
|
||||
void _IPCDriver_SetupCmd_IOSOpen(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor, const char* devicePath, uint32 flags)
|
||||
{
|
||||
// store the path in the buffer after the command body
|
||||
IPCResourceBuffer* resBuffer = requestDescriptor->resourcePtr;
|
||||
IPCCommandBody& cmdBody = resBuffer->commandBody;
|
||||
|
||||
uint8* buffer = resBuffer->bufferData;
|
||||
size_t pathLen = strlen(devicePath);
|
||||
if (pathLen > 31)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IOS_Open(): Device path must not exceed 31 characters");
|
||||
cemu_assert_error();
|
||||
}
|
||||
memcpy(buffer, devicePath, pathLen + 1);
|
||||
|
||||
cmdBody.ppcVirt0 = MEMPTR<void>(buffer).GetMPTR();
|
||||
cmdBody.args[0] = 0;
|
||||
cmdBody.args[1] = (uint32)(pathLen + 1);
|
||||
cmdBody.args[2] = flags;
|
||||
}
|
||||
|
||||
IOS_ERROR _IPCDriver_SetupCmd_IOSIoctl(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut)
|
||||
{
|
||||
IPCCommandBody& cmdBody = requestDescriptor->resourcePtr->commandBody;
|
||||
cmdBody.args[0] = requestId;
|
||||
cmdBody.args[1] = MPTR_NULL; // set to ppcVirt0 later
|
||||
cmdBody.args[2] = sizeIn;
|
||||
cmdBody.args[3] = MPTR_NULL; // set to ppcVirt1 later
|
||||
cmdBody.args[4] = sizeOut;
|
||||
cmdBody.ppcVirt0 = MEMPTR<void>(ptrIn).GetMPTR();
|
||||
cmdBody.ppcVirt1 = MEMPTR<void>(ptrOut).GetMPTR();
|
||||
return IOS_ERROR_OK;
|
||||
}
|
||||
|
||||
IOS_ERROR _IPCDriver_SetupCmd_IOSIoctlv(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec)
|
||||
{
|
||||
IPCCommandBody& cmdBody = requestDescriptor->resourcePtr->commandBody;
|
||||
// verify input and output vectors
|
||||
IPCIoctlVector* vecIn = vec;
|
||||
IPCIoctlVector* vecOut = vec + numIn;
|
||||
for (uint32 i = 0; i < numIn; i++)
|
||||
{
|
||||
if (vecIn[i].baseVirt == nullptr && vecIn[i].size != 0)
|
||||
return IOS_ERROR_INVALID_ARG;
|
||||
vecIn[i].basePhys = vecIn[i].baseVirt;
|
||||
vecIn[i].baseVirt = nullptr;
|
||||
}
|
||||
for (uint32 i = 0; i < numOut; i++)
|
||||
{
|
||||
if (vecOut[i].baseVirt == nullptr && vecOut[i].size != 0)
|
||||
return IOS_ERROR_INVALID_ARG;
|
||||
vecOut[i].basePhys = vecOut[i].baseVirt;
|
||||
vecOut[i].baseVirt = nullptr;
|
||||
}
|
||||
// set args
|
||||
cmdBody.ppcVirt0 = MEMPTR<void>(vec).GetMPTR();
|
||||
cmdBody.args[0] = requestId;
|
||||
cmdBody.args[1] = numIn;
|
||||
cmdBody.args[2] = numOut;
|
||||
cmdBody.args[3] = 0; // set to ppcVirt0 later
|
||||
return IOS_ERROR_OK;
|
||||
}
|
||||
|
||||
IOSDevHandle IOS_Open(const char* devicePath, uint32 flags)
|
||||
{
|
||||
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
|
||||
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, 0, IPCCommandId::IOS_OPEN, nullptr, nullptr, nullptr);
|
||||
_IPCDriver_SetupCmd_IOSOpen(ipcDriver, ipcDescriptor, devicePath, flags);
|
||||
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
|
||||
uint32 r = _IPCDriver_WaitForResultAndRelease(ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
|
||||
IOS_ERROR IOS_Close(IOSDevHandle devHandle)
|
||||
{
|
||||
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
|
||||
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_CLOSE, nullptr, nullptr, nullptr);
|
||||
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
|
||||
IOS_ERROR r = (IOS_ERROR)_IPCDriver_WaitForResultAndRelease(ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
|
||||
IOS_ERROR IOS_Ioctl(IOSDevHandle devHandle, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut)
|
||||
{
|
||||
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
|
||||
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_IOCTL, nullptr, nullptr, nullptr);
|
||||
IOS_ERROR r = _IPCDriver_SetupCmd_IOSIoctl(ipcDriver, ipcDescriptor, requestId, ptrIn, sizeIn, ptrOut, sizeOut);
|
||||
if (r != IOS_ERROR_OK)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IOS_Ioctl failed due to bad parameters");
|
||||
IPCDriver_ReleaseResource(&ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
|
||||
r = (IOS_ERROR)_IPCDriver_WaitForResultAndRelease(ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
|
||||
IOS_ERROR IOS_IoctlAsync(IOSDevHandle devHandle, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam)
|
||||
{
|
||||
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
|
||||
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_IOCTL, nullptr, asyncResultFunc, asyncResultUserParam);
|
||||
IOS_ERROR r = _IPCDriver_SetupCmd_IOSIoctl(ipcDriver, ipcDescriptor, requestId, ptrIn, sizeIn, ptrOut, sizeOut);
|
||||
if (r != IOS_ERROR_OK)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IOS_Ioctl failed due to bad parameters");
|
||||
IPCDriver_ReleaseResource(&ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
|
||||
IOS_ERROR IOS_Ioctlv(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec)
|
||||
{
|
||||
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
|
||||
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_IOCTLV, nullptr, nullptr, nullptr);
|
||||
IOS_ERROR r = _IPCDriver_SetupCmd_IOSIoctlv(ipcDriver, ipcDescriptor, requestId, numIn, numOut, vec);
|
||||
if (r != IOS_ERROR_OK)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IOS_Ioctlv failed due to bad parameters");
|
||||
IPCDriver_ReleaseResource(&ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
|
||||
r = (IOS_ERROR)_IPCDriver_WaitForResultAndRelease(ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
|
||||
IOS_ERROR IOS_IoctlvAsync(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam)
|
||||
{
|
||||
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
|
||||
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_IOCTLV, nullptr, asyncResultFunc, asyncResultUserParam);
|
||||
IOS_ERROR r = _IPCDriver_SetupCmd_IOSIoctlv(ipcDriver, ipcDescriptor, requestId, numIn, numOut, vec);
|
||||
if (r != IOS_ERROR_OK)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IOS_Ioctlv failed due to bad parameters");
|
||||
IPCDriver_ReleaseResource(&ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
|
||||
return r;
|
||||
}
|
||||
|
||||
void InitializeIPC()
|
||||
{
|
||||
for (uint32 i = 0; i < Espresso::CORE_COUNT; i++)
|
||||
{
|
||||
IPCDriver_InitForCore(i);
|
||||
IPCDriver_InitIPCThread(i);
|
||||
}
|
||||
|
||||
// register API
|
||||
cafeExportRegister("coreinit", IOS_Open, LogType::PPC_IPC);
|
||||
cafeExportRegister("coreinit", IOS_Close, LogType::PPC_IPC);
|
||||
cafeExportRegister("coreinit", IOS_Ioctl, LogType::PPC_IPC);
|
||||
cafeExportRegister("coreinit", IOS_IoctlAsync, LogType::PPC_IPC);
|
||||
cafeExportRegister("coreinit", IOS_Ioctlv, LogType::PPC_IPC);
|
||||
cafeExportRegister("coreinit", IOS_IoctlvAsync, LogType::PPC_IPC);
|
||||
}
|
||||
|
||||
};
|
||||
16
src/Cafe/OS/libs/coreinit/coreinit_IPC.h
Normal file
16
src/Cafe/OS/libs/coreinit/coreinit_IPC.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#include "Cafe/IOSU/iosu_ipc_common.h"
|
||||
#include "Cafe/IOSU/iosu_types_common.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void IPCDriver_NotifyResponses(uint32 ppcCoreIndex, IPCCommandBody** responseArray, uint32 numResponses);
|
||||
|
||||
IOSDevHandle IOS_Open(const char* devicePath, uint32 flags);
|
||||
IOS_ERROR IOS_Close(IOSDevHandle devHandle);
|
||||
IOS_ERROR IOS_Ioctl(IOSDevHandle devHandle, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut);
|
||||
IOS_ERROR IOS_IoctlAsync(IOSDevHandle devHandle, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam);
|
||||
IOS_ERROR IOS_Ioctlv(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec);
|
||||
IOS_ERROR IOS_IoctlvAsync(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam);
|
||||
|
||||
void InitializeIPC();
|
||||
};
|
||||
233
src/Cafe/OS/libs/coreinit/coreinit_IPCBuf.cpp
Normal file
233
src/Cafe/OS/libs/coreinit/coreinit_IPCBuf.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
struct FIFOEntry_t
|
||||
{
|
||||
MEMPTR<uint8> p;
|
||||
};
|
||||
|
||||
struct IPCFifo_t
|
||||
{
|
||||
uint32be writeIndex;
|
||||
uint32be readIndex;
|
||||
uint32be availableEntries; // number of available entries
|
||||
uint32be entryCount;
|
||||
MEMPTR<FIFOEntry_t> entryArray;
|
||||
};
|
||||
|
||||
struct IPCBufPool_t
|
||||
{
|
||||
/* +0x00 */ uint32be magic;
|
||||
/* +0x04 */ MEMPTR<void> fullBufferPtr;
|
||||
/* +0x08 */ uint32be fullBufferSize;
|
||||
/* +0x0C */ uint32be uknFromParamR7; // boolean?
|
||||
/* +0x10 */ uint32be ukn10; // set to zero on init
|
||||
/* +0x14 */ uint32be entrySize1;
|
||||
/* +0x18 */ uint32be entrySize2; // set to same value as entrySize1
|
||||
/* +0x1C */ uint32be entryCount; // actual number of used entries
|
||||
/* +0x20 */ MEMPTR<uint8> entryStartPtr;
|
||||
/* +0x24 */ uint32be entryCountMul4;
|
||||
/* +0x28 */ IPCFifo_t fifo;
|
||||
/* +0x3C */ coreinit::OSMutex mutex;
|
||||
// full size is 0x68
|
||||
};
|
||||
|
||||
void FIFOInit(IPCFifo_t* fifo, uint32 entryCount, void* entryArray)
|
||||
{
|
||||
fifo->entryCount = entryCount;
|
||||
fifo->entryArray = (FIFOEntry_t*)entryArray;
|
||||
fifo->writeIndex = 0;
|
||||
fifo->readIndex = -1;
|
||||
fifo->availableEntries = 0;
|
||||
}
|
||||
|
||||
sint32 FIFOPush(IPCFifo_t* fifo, void* entry)
|
||||
{
|
||||
if (fifo->readIndex == fifo->writeIndex)
|
||||
{
|
||||
assert_dbg();
|
||||
return -8;
|
||||
}
|
||||
fifo->entryArray[(uint32)fifo->writeIndex].p = (uint8*)entry;
|
||||
if ((sint32)fifo->readIndex < 0)
|
||||
{
|
||||
// set readIndex to valid value when fifo was empty
|
||||
fifo->readIndex = fifo->writeIndex;
|
||||
}
|
||||
fifo->availableEntries = (uint32)fifo->availableEntries + 1;
|
||||
fifo->writeIndex = ((uint32)fifo->writeIndex + 1) % (uint32)fifo->entryCount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sint32 FIFOPop(IPCFifo_t* fifo, uint8** entry)
|
||||
{
|
||||
*entry = nullptr;
|
||||
if ((sint32)fifo->readIndex < 0)
|
||||
return -7;
|
||||
fifo->availableEntries = (uint32)fifo->availableEntries - 1;
|
||||
*entry = fifo->entryArray[(uint32)fifo->readIndex].p.GetPtr();
|
||||
fifo->readIndex = ((uint32)fifo->readIndex + 1) % (uint32)fifo->entryCount;
|
||||
|
||||
if (fifo->availableEntries == (uint32be)0)
|
||||
{
|
||||
fifo->readIndex = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* getUpwardsAlignedAddr(void* addr, uint32 alignment)
|
||||
{
|
||||
uint64 v = ((uint64)addr + alignment - 1) & ~(uint64)(alignment - 1);
|
||||
return (uint8*)v;
|
||||
}
|
||||
|
||||
void* getDownwardsAlignedAddr(void* addr, uint32 alignment)
|
||||
{
|
||||
uint64 v = ((uint64)addr) & ~(uint64)(alignment - 1);
|
||||
return (uint8*)v;
|
||||
}
|
||||
|
||||
static_assert(sizeof(IPCBufPool_t) == 0x68);
|
||||
|
||||
IPCBufPool_t* IPCBufPoolCreate(uint8* bufferArea, uint32 bufferSize, uint32 entrySize, uint32be* entryCountOutput, uint32 uknR7)
|
||||
{
|
||||
memset(bufferArea, 0, bufferSize);
|
||||
IPCBufPool_t* ipcBufPool = (IPCBufPool_t*)getUpwardsAlignedAddr(bufferArea, 4);
|
||||
uint8* alignedEnd = (uint8*)getDownwardsAlignedAddr(bufferArea + bufferSize, 4);
|
||||
uint8* dataStart = (uint8*)getUpwardsAlignedAddr(ipcBufPool + 1, 0x4);
|
||||
|
||||
*entryCountOutput = 0;
|
||||
// todo: Validate parameters
|
||||
|
||||
OSInitMutexEx(&ipcBufPool->mutex, NULL);
|
||||
|
||||
ipcBufPool->fullBufferPtr = bufferArea;
|
||||
ipcBufPool->fullBufferSize = bufferSize;
|
||||
ipcBufPool->uknFromParamR7 = uknR7;
|
||||
|
||||
uint32 paddedEntrySize = (entrySize + 0x3F) & ~0x3F;
|
||||
|
||||
ipcBufPool->ukn10 = 0;
|
||||
ipcBufPool->magic = 0xBADF00D;
|
||||
|
||||
ipcBufPool->entrySize1 = paddedEntrySize;
|
||||
ipcBufPool->entrySize2 = paddedEntrySize;
|
||||
|
||||
uint32 remainingSize = (uint32)((bufferArea + bufferSize) - dataStart);
|
||||
uint32 entryCount = remainingSize / paddedEntrySize;
|
||||
if (entryCount <= 1)
|
||||
assert_dbg();
|
||||
|
||||
ipcBufPool->entryCountMul4 = entryCount * 4;
|
||||
if ((uint32)ipcBufPool->entryCountMul4 >= paddedEntrySize)
|
||||
{
|
||||
// special handling required (need to adjust entry count?)
|
||||
assert_dbg();
|
||||
}
|
||||
else
|
||||
{
|
||||
entryCount--; // remove one entry to make room for entry pointer array
|
||||
ipcBufPool->entryCount = entryCount;
|
||||
*entryCountOutput = entryCount;
|
||||
ipcBufPool->entryStartPtr = (dataStart + (uint32)ipcBufPool->entryCountMul4);
|
||||
FIFOInit(&ipcBufPool->fifo, (uint32)ipcBufPool->entryCount, dataStart);
|
||||
}
|
||||
// add all entries to the fifo
|
||||
for (sint32 i = 0; i < (sint32)ipcBufPool->entryCount; i++)
|
||||
{
|
||||
uint8* entry = ipcBufPool->entryStartPtr.GetPtr() + i * (uint32)ipcBufPool->entrySize2;
|
||||
if (FIFOPush(&ipcBufPool->fifo, entry) != 0)
|
||||
return nullptr;
|
||||
}
|
||||
return ipcBufPool;
|
||||
}
|
||||
|
||||
uint8* IPCBufPoolAllocate(IPCBufPool_t* ipcBufPool, uint32 size)
|
||||
{
|
||||
uint8* entry = nullptr;
|
||||
OSLockMutex(&ipcBufPool->mutex);
|
||||
if (ipcBufPool->magic == (uint32be)0xBADF00D && size <= (uint32)ipcBufPool->entrySize1)
|
||||
{
|
||||
FIFOPop(&ipcBufPool->fifo, &entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_dbg();
|
||||
}
|
||||
OSUnlockMutex(&ipcBufPool->mutex);
|
||||
return entry;
|
||||
}
|
||||
|
||||
sint32 IPCBufPoolFree(IPCBufPool_t* ipcBufPool, uint8* entry)
|
||||
{
|
||||
OSLockMutex(&ipcBufPool->mutex);
|
||||
sint32 res = 0;
|
||||
if (ipcBufPool->magic == (uint32be)0xBADF00D)
|
||||
{
|
||||
// check if entry is actually part of the pool
|
||||
uint32 offset = (uint32)(entry - (uint8*)ipcBufPool->entryStartPtr.GetPtr());
|
||||
if ((offset % (uint32)ipcBufPool->entrySize2) != 0)
|
||||
assert_dbg();
|
||||
uint32 index = offset / (uint32)ipcBufPool->entrySize2;
|
||||
if ((index >= (uint32)ipcBufPool->entryCount))
|
||||
assert_dbg();
|
||||
FIFOPush(&ipcBufPool->fifo, entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_dbg();
|
||||
res = -4;
|
||||
}
|
||||
OSUnlockMutex(&ipcBufPool->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
void coreinitExport_IPCBufPoolCreate(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamTypePtr(bufferArea, uint8, 0);
|
||||
ppcDefineParamU32(bufferSize, 1);
|
||||
ppcDefineParamU32(entrySize, 2);
|
||||
ppcDefineParamU32BEPtr(entryCountOutput, 3);
|
||||
ppcDefineParamU32(uknR7, 4);
|
||||
IPCBufPool_t* ipcBufPool = IPCBufPoolCreate(bufferArea, bufferSize, entrySize, entryCountOutput, uknR7);
|
||||
osLib_returnFromFunction(hCPU, memory_getVirtualOffsetFromPointer(ipcBufPool));
|
||||
return;
|
||||
// example dump of IPC buffer (at 0x1011FF40):
|
||||
// 0000 0B AD F0 0D 10 11 FF 40 00 00 53 01 00 00 00 01 00 00 00 00 00 00 02 C0 00 00 02 C0 00 00 00 1D
|
||||
// 0020 10 12 00 40 00 00 00 78 00 00 00 00 00 00 00 00 00 00 00 1D 00 00 00 1D 10 11 FF A8 6D 55 74 58
|
||||
// 0040 10 00 87 2C 00 00 00 00 00 00 00 00 00 00 00 00 10 11 FF 7C 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
// 0060 00 00 00 00 00 00 00 00 10 12 00 40 10 12 03 00 10 12 05 C0 10 12 08 80 10 12 0B 40 10 12 0E 00
|
||||
// 0080 10 12 10 C0 10 12 13 80 10 12 16 40 10 12 19 00 10 12 1B C0 10 12 1E 80 10 12 21 40 10 12 24 00
|
||||
// 00A0 10 12 26 C0 10 12 29 80 10 12 2C 40 10 12 2F 00 10 12 31 C0 10 12 34 80 10 12 37 40 10 12 3A 00
|
||||
// 00C0 10 12 3C C0 10 12 3F 80 10 12 42 40 10 12 45 00 10 12 47 C0 10 12 4A 80 10 12 4D 40 00 00 00 00
|
||||
// 00E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
}
|
||||
|
||||
void coreinitExport_IPCBufPoolAllocate(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("IPCBufPoolAllocate(0x%08x,0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
ppcDefineParamStructPtr(ipcBufPool, IPCBufPool_t, 0);
|
||||
ppcDefineParamU32(size, 1);
|
||||
uint8* entry = IPCBufPoolAllocate(ipcBufPool, size);
|
||||
osLib_returnFromFunction(hCPU, memory_getVirtualOffsetFromPointer(entry));
|
||||
}
|
||||
|
||||
void coreinitExport_IPCBufPoolFree(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("IPCBufPoolFree(0x%08x,0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
ppcDefineParamStructPtr(ipcBufPool, IPCBufPool_t, 0);
|
||||
ppcDefineParamTypePtr(entry, uint8, 1);
|
||||
sint32 res = IPCBufPoolFree(ipcBufPool, entry);
|
||||
osLib_returnFromFunction(hCPU, res);
|
||||
}
|
||||
|
||||
void InitializeIPCBuf()
|
||||
{
|
||||
osLib_addFunction("coreinit", "IPCBufPoolCreate", coreinitExport_IPCBufPoolCreate);
|
||||
osLib_addFunction("coreinit", "IPCBufPoolAllocate", coreinitExport_IPCBufPoolAllocate);
|
||||
osLib_addFunction("coreinit", "IPCBufPoolFree", coreinitExport_IPCBufPoolFree);
|
||||
}
|
||||
}
|
||||
6
src/Cafe/OS/libs/coreinit/coreinit_IPCBuf.h
Normal file
6
src/Cafe/OS/libs/coreinit/coreinit_IPCBuf.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitializeIPCBuf();
|
||||
}
|
||||
198
src/Cafe/OS/libs/coreinit/coreinit_Init.cpp
Normal file
198
src/Cafe/OS/libs/coreinit/coreinit_Init.cpp
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/HW/Espresso/PPCCallback.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
#include "Cafe/OS/libs/padscore/padscore.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_SysHeap.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
|
||||
#include "Cafe/OS/libs/vpad/vpad.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_GHS.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
|
||||
extern MPTR _entryPoint;
|
||||
extern RPLModule* applicationRPX;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MPTR argv[32];
|
||||
uint32be argc;
|
||||
char argStorage[0x1000];
|
||||
}coreinitInit_t;
|
||||
|
||||
coreinitInit_t* _coreinitInfo = nullptr;
|
||||
|
||||
MPTR OSAllocFromSystem(uint32 size, uint32 alignment)
|
||||
{
|
||||
return coreinit_allocFromSysArea(size, alignment);
|
||||
}
|
||||
|
||||
void OSFreeToSystem(MPTR mem)
|
||||
{
|
||||
coreinit_freeToSysArea(mem);
|
||||
}
|
||||
|
||||
extern std::string _pathToExecutable;
|
||||
sint32 argStorageIndex;
|
||||
|
||||
void _AddArg(const char* arg, sint32 len)
|
||||
{
|
||||
uint32 argc = _coreinitInfo->argc;
|
||||
|
||||
char* argStorageStr = _coreinitInfo->argStorage + argStorageIndex;
|
||||
memcpy(argStorageStr, arg, len);
|
||||
argStorageStr[len] = '\0';
|
||||
argStorageIndex += (sint32)strlen(arg) + 1;
|
||||
|
||||
_coreinitInfo->argv[argc] = _swapEndianU32(memory_getVirtualOffsetFromPointer(argStorageStr));
|
||||
_coreinitInfo->argc = argc+1;
|
||||
}
|
||||
|
||||
sint32 _GetArgLength(const char* arg)
|
||||
{
|
||||
sint32 c = 0;
|
||||
while (*arg)
|
||||
{
|
||||
if (*arg == ' ')
|
||||
break; // end at whitespace
|
||||
cemu_assert_debug(*arg != '\"' && *arg != '\''); // todo
|
||||
arg++;
|
||||
c++;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void CafeInit()
|
||||
{
|
||||
// extract executable filename
|
||||
sint32 rpxPathStart = (sint32)_pathToExecutable.size() - 1;
|
||||
if (rpxPathStart > 0)
|
||||
{
|
||||
while (rpxPathStart > 0 && _pathToExecutable[rpxPathStart-1] != '/')
|
||||
rpxPathStart--;
|
||||
}
|
||||
else
|
||||
{
|
||||
rpxPathStart = 0;
|
||||
}
|
||||
|
||||
std::string_view rpxFileName = std::basic_string_view<char>(_pathToExecutable.data() + rpxPathStart, _pathToExecutable.data() + _pathToExecutable.size());
|
||||
|
||||
argStorageIndex = 0;
|
||||
_coreinitInfo->argc = 0;
|
||||
_AddArg(rpxFileName.data(), rpxFileName.size());
|
||||
strcpy((char*)_coreinitInfo->argStorage, std::string(rpxFileName).c_str());
|
||||
|
||||
std::string _argStr = CafeSystem::GetForegroundTitleArgStr();
|
||||
const char* argString = _argStr.c_str();
|
||||
// attach parameters from arg string
|
||||
if (argString && argString[0] != '\0')
|
||||
{
|
||||
const char* t = strstr(argString, ".rpx");
|
||||
if (t)
|
||||
{
|
||||
t += 4;
|
||||
while (*t)
|
||||
{
|
||||
// skip all whitespace
|
||||
while (*t)
|
||||
{
|
||||
if (*t == ' ')
|
||||
{
|
||||
t++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
// get length of arg
|
||||
sint32 argLength = _GetArgLength(t);
|
||||
if (argLength > 0)
|
||||
{
|
||||
// add arg
|
||||
_AddArg(t, argLength);
|
||||
// next
|
||||
t += argLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
forceLogDebug_printf("Unable to find end of rpx file name in arg string");
|
||||
}
|
||||
}
|
||||
// setup UGQR
|
||||
ppcInterpreterCurrentInstance->spr.UGQR[0 + 2] = 0x00040004;
|
||||
ppcInterpreterCurrentInstance->spr.UGQR[0 + 3] = 0x00050005;
|
||||
ppcInterpreterCurrentInstance->spr.UGQR[0 + 4] = 0x00060006;
|
||||
ppcInterpreterCurrentInstance->spr.UGQR[0 + 5] = 0x00070007;
|
||||
coreinit::InitForegroundBucket();
|
||||
coreinit::InitSysHeap();
|
||||
}
|
||||
|
||||
struct PreinitUserHeapStruct
|
||||
{
|
||||
MEMPTR<coreinit::MEMHeapBase> heapTempMEM1;
|
||||
MEMPTR<coreinit::MEMHeapBase> heapTempFG;
|
||||
MEMPTR<coreinit::MEMHeapBase> heapTempMEM2;
|
||||
};
|
||||
|
||||
SysAllocator<PreinitUserHeapStruct> g_preinitUserParam;
|
||||
|
||||
void InitCafeHeaps()
|
||||
{
|
||||
// init default heaps
|
||||
g_preinitUserParam->heapTempMEM1 = nullptr;
|
||||
g_preinitUserParam->heapTempFG = nullptr;
|
||||
g_preinitUserParam->heapTempMEM2 = nullptr;
|
||||
coreinit::InitDefaultHeaps(g_preinitUserParam->heapTempMEM1, g_preinitUserParam->heapTempFG, g_preinitUserParam->heapTempMEM2);
|
||||
// if __preinit_user export exists in main executable, run it and pass our heaps
|
||||
MPTR exportAddr = applicationRPX ? RPLLoader_FindRPLExport(applicationRPX, "__preinit_user", false) : MPTR_NULL;
|
||||
if (exportAddr != MPTR_NULL)
|
||||
{
|
||||
PPCCoreCallback(exportAddr, &g_preinitUserParam->heapTempMEM1, &g_preinitUserParam->heapTempFG, &g_preinitUserParam->heapTempMEM2);
|
||||
}
|
||||
// setup heaps
|
||||
if (g_preinitUserParam->heapTempMEM1 != nullptr)
|
||||
coreinit::MEMSetBaseHeapHandle(0, g_preinitUserParam->heapTempMEM1);
|
||||
if (g_preinitUserParam->heapTempFG != nullptr)
|
||||
coreinit::MEMSetBaseHeapHandle(8, g_preinitUserParam->heapTempFG);
|
||||
if (g_preinitUserParam->heapTempMEM2 != nullptr)
|
||||
coreinit::MEMSetBaseHeapHandle(1, g_preinitUserParam->heapTempMEM2);
|
||||
}
|
||||
|
||||
MPTR CoreInitEntry(sint32 argc, MPTR argv)
|
||||
{
|
||||
const char* rpxPath = (char*)memory_getPointerFromVirtualOffset(memory_readU32(argv + 0));
|
||||
InitCafeHeaps();
|
||||
// do a dummy allocation via the OSDynLoad allocator
|
||||
// Watch Dogs relies on this to correctly set up its malloc() allocator
|
||||
// must be larger than 0x190 to trigger creation of a new memory segment. But also must not be close to page alignment (0x1000) or else the bug will trigger
|
||||
void* dummyAlloc = coreinit::OSDynLoad_AllocatorAlloc(0x500, 0x4);
|
||||
if (dummyAlloc)
|
||||
coreinit::OSDynLoad_AllocatorFree(dummyAlloc);
|
||||
return _entryPoint;
|
||||
}
|
||||
|
||||
sint32 _coreinitTitleEntryPoint;
|
||||
|
||||
void coreinit_start(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
_coreinitInfo = (coreinitInit_t*)memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sizeof(coreinitInit_t), 32));
|
||||
memset(_coreinitInfo, 0, sizeof(coreinitInit_t));
|
||||
|
||||
CafeInit();
|
||||
_coreinitTitleEntryPoint = CoreInitEntry(_coreinitInfo->argc, memory_getVirtualOffsetFromPointer(_coreinitInfo->argv));
|
||||
|
||||
RPLLoader_CallEntrypoints();
|
||||
|
||||
// init vpadbase (todo - simulate entrypoints for HLE modules)
|
||||
padscore::start();
|
||||
vpad::start();
|
||||
|
||||
// continue at main executable entrypoint
|
||||
hCPU->gpr[4] = memory_getVirtualOffsetFromPointer(_coreinitInfo->argv);
|
||||
hCPU->gpr[3] = _coreinitInfo->argc;
|
||||
hCPU->instructionPointer = _coreinitTitleEntryPoint;
|
||||
}
|
||||
299
src/Cafe/OS/libs/coreinit/coreinit_LockedCache.cpp
Normal file
299
src/Cafe/OS/libs/coreinit/coreinit_LockedCache.cpp
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
|
||||
|
||||
/*
|
||||
Locked cache is mapped to the following memory regions:
|
||||
offset size
|
||||
core0: 0xFFC00000 0x4000
|
||||
core1: 0xFFC40000 0x4000
|
||||
core2: 0xFFC80000 0x4000
|
||||
*/
|
||||
|
||||
#define LC_LOCKED_CACHE_GRANULARITY (0x200) // 512B
|
||||
#define LC_LOCKED_CACHE_SIZE (0x4000) // 16KB
|
||||
|
||||
#define LC_MASK_FREE (0)
|
||||
#define LC_MASK_RESERVED (1)
|
||||
#define LC_MASK_RESERVED_END (2) // indicates end of reserved block
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
uint8 lcCacheMask[PPC_CORE_COUNT][(LC_LOCKED_CACHE_SIZE + LC_LOCKED_CACHE_GRANULARITY - 1) / LC_LOCKED_CACHE_GRANULARITY] = { 0 };
|
||||
uint32 lcAllocatedBlocks[PPC_CORE_COUNT] = { 0 };
|
||||
|
||||
MPTR lcAddr[] =
|
||||
{
|
||||
0xFFC00000, // core 0
|
||||
0xFFC40000, // core 1
|
||||
0xFFC80000 // core 2
|
||||
};
|
||||
|
||||
void coreinitExport_LCGetMaxSize(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
osLib_returnFromFunction(hCPU, LC_LOCKED_CACHE_SIZE);
|
||||
}
|
||||
|
||||
void coreinitExport_LCAlloc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
//debug_printf("LCAlloc(0x%04x) Thread %08x Core %d", hCPU->gpr[3], coreinitThread_getCurrentThread(hCPU), PPCInterpreter_getCoreIndex(hCPU));
|
||||
uint32 size = hCPU->gpr[3];
|
||||
if (size == 0 || size > LC_LOCKED_CACHE_SIZE)
|
||||
{
|
||||
debug_printf("LCAlloc: Invalid alloc size %d\n", size);
|
||||
osLib_returnFromFunction(hCPU, MPTR_NULL);
|
||||
return;
|
||||
}
|
||||
if ((size % LC_LOCKED_CACHE_GRANULARITY) != 0)
|
||||
{
|
||||
debug_printf("LCAlloc: Unaligned alloc size 0x%04x\n", size);
|
||||
size += (LC_LOCKED_CACHE_GRANULARITY - 1);
|
||||
size &= ~(LC_LOCKED_CACHE_GRANULARITY - 1);
|
||||
}
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
|
||||
uint8* cacheMask = lcCacheMask[coreIndex];
|
||||
uint32 entryMaskLength = size / LC_LOCKED_CACHE_GRANULARITY;
|
||||
for (uint32 i = 0; i <= (LC_LOCKED_CACHE_SIZE / LC_LOCKED_CACHE_GRANULARITY) - entryMaskLength; i++)
|
||||
{
|
||||
// check if range starting at i is free
|
||||
bool rangeIsFree = true;
|
||||
for (uint32 f = 0; f < entryMaskLength; f++)
|
||||
{
|
||||
if (cacheMask[i + f] != LC_MASK_FREE)
|
||||
{
|
||||
rangeIsFree = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rangeIsFree)
|
||||
{
|
||||
// found space for allocation
|
||||
MPTR allocAddr = lcAddr[coreIndex] + i * LC_LOCKED_CACHE_GRANULARITY;
|
||||
// mark range as allocated
|
||||
for (uint32 f = 0; f < entryMaskLength - 1; f++)
|
||||
{
|
||||
cacheMask[i + f] = LC_MASK_RESERVED;
|
||||
}
|
||||
cacheMask[i + entryMaskLength - 1] = LC_MASK_RESERVED_END;
|
||||
// update allocation counter
|
||||
lcAllocatedBlocks[coreIndex] += entryMaskLength;
|
||||
// return allocAddr
|
||||
//debug_printf("LCAlloc result %08x Thread %08x Core %d", (uint32)allocAddr, coreinitThread_getCurrentThread(hCPU), PPCInterpreter_getCoreIndex(hCPU));
|
||||
osLib_returnFromFunction(hCPU, allocAddr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// not enough space left
|
||||
//debug_printf("LCAlloc failed Thread %08x Core %d", coreinitThread_getCurrentThread(hCPU), PPCInterpreter_getCoreIndex(hCPU));
|
||||
osLib_returnFromFunction(hCPU, MPTR_NULL);
|
||||
}
|
||||
|
||||
|
||||
void coreinitExport_LCDealloc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
|
||||
uint8* cacheMask = lcCacheMask[coreIndex];
|
||||
//printf("LCDealloc(0x%08x)\n", hCPU->gpr[3]);
|
||||
|
||||
MPTR deallocAddr = hCPU->gpr[3];
|
||||
if (deallocAddr < lcAddr[coreIndex] || deallocAddr >= (lcAddr[coreIndex] + LC_LOCKED_CACHE_SIZE))
|
||||
{
|
||||
// out of bounds
|
||||
#ifndef PUBLIC_RELEASE
|
||||
forceLog_printf("LCDealloc(): Out of bounds");
|
||||
#endif
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
uint32 relativeOffset = (uint32)deallocAddr - lcAddr[coreIndex];
|
||||
if ((relativeOffset % LC_LOCKED_CACHE_GRANULARITY) != 0)
|
||||
{
|
||||
debug_printf("LCDealloc: Offset alignment is invalid (0x%08x / 0x%08x)\n", deallocAddr, relativeOffset);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
uint32 startIndex = relativeOffset / LC_LOCKED_CACHE_GRANULARITY;
|
||||
// check this is really the beginning of an allocated block
|
||||
if (startIndex > 0 && (cacheMask[startIndex - 1] != LC_MASK_RESERVED_END && cacheMask[startIndex - 1] != LC_MASK_FREE))
|
||||
{
|
||||
debug_printf("LCDealloc: Offset is invalid (0x%08x / 0x%08x)\n", deallocAddr, relativeOffset);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
// free range by reseting mask in allocated region
|
||||
for (uint32 i = startIndex; i < (LC_LOCKED_CACHE_SIZE / LC_LOCKED_CACHE_GRANULARITY); i++)
|
||||
{
|
||||
if (cacheMask[i] == LC_MASK_RESERVED_END)
|
||||
{
|
||||
// end of allocated block reached
|
||||
cacheMask[i] = LC_MASK_FREE;
|
||||
// update allocation counter
|
||||
lcAllocatedBlocks[coreIndex]--;
|
||||
break;
|
||||
}
|
||||
else if (cacheMask[i] == LC_MASK_FREE)
|
||||
{
|
||||
debug_printf("LCDealloc: Allocation mask error detected\n");
|
||||
break;
|
||||
}
|
||||
cacheMask[i] = LC_MASK_FREE;
|
||||
// update allocation counter
|
||||
lcAllocatedBlocks[coreIndex]--;
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_LCGetUnallocated(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint32 unallocatedBytes = 0;
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
|
||||
unallocatedBytes = LC_LOCKED_CACHE_SIZE - (lcAllocatedBlocks[coreIndex] * LC_LOCKED_CACHE_GRANULARITY);
|
||||
//debug_printf("LCGetUnallocated() Result: 0x%x\n", unallocatedBytes);
|
||||
osLib_returnFromFunction(hCPU, unallocatedBytes);
|
||||
}
|
||||
|
||||
void coreinitExport_LCGetAllocatableSize(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("LCGetAllocatableSize()\n");
|
||||
// no parameters, returns largest allocatable block size
|
||||
// get core LC parameters
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
|
||||
uint8* cacheMask = lcCacheMask[coreIndex];
|
||||
// scan for largest range of available blocks
|
||||
uint32 largestFreeRange = 0;
|
||||
uint32 currentRangeSize = 0;
|
||||
for (uint32 i = 0; i < LC_LOCKED_CACHE_SIZE / LC_LOCKED_CACHE_GRANULARITY; i++)
|
||||
{
|
||||
if (cacheMask[i] == LC_MASK_FREE)
|
||||
{
|
||||
currentRangeSize++;
|
||||
}
|
||||
else
|
||||
{
|
||||
largestFreeRange = std::max(largestFreeRange, currentRangeSize);
|
||||
currentRangeSize = 0;
|
||||
}
|
||||
}
|
||||
largestFreeRange = std::max(largestFreeRange, currentRangeSize);
|
||||
osLib_returnFromFunction(hCPU, largestFreeRange * LC_LOCKED_CACHE_GRANULARITY);
|
||||
}
|
||||
|
||||
uint32 LCIsEnabled[PPC_CORE_COUNT] = { 0 };
|
||||
|
||||
void coreinitExport_LCEnableDMA(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
//debug_printf("LCEnableDMA()\n");
|
||||
|
||||
LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)]++;
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
}
|
||||
|
||||
sint32 _lcDisableErrorCounter = 0;
|
||||
|
||||
void coreinitExport_LCDisableDMA(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
//debug_printf("LCDisableDMA()\n");
|
||||
#ifndef PUBLIC_RELASE
|
||||
if (LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)] == 0)
|
||||
assert_dbg();
|
||||
#endif
|
||||
LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)]--;
|
||||
#ifndef PUBLIC_RELEASE
|
||||
if (LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)] == 0)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
|
||||
uint8* cacheMask = lcCacheMask[coreIndex];
|
||||
for (uint32 i = 0; i <= (LC_LOCKED_CACHE_SIZE / LC_LOCKED_CACHE_GRANULARITY); i++)
|
||||
{
|
||||
if (cacheMask[i] != LC_MASK_FREE)
|
||||
{
|
||||
if (_lcDisableErrorCounter < 15)
|
||||
{
|
||||
forceLog_printf("LC disabled but there is still memory allocated");
|
||||
_lcDisableErrorCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
}
|
||||
|
||||
void coreinitExport_LCIsDMAEnabled(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
osLib_returnFromFunction(hCPU, LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)] ? 1 : 0);
|
||||
}
|
||||
|
||||
void coreinitExport_LCHardwareIsAvailable(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// on the real HW, LC is shared between processes and not all processes can access it. In the emulator we don't care and always return true.
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
}
|
||||
|
||||
void coreinitExport_LCLoadDMABlocks(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
//printf("LCLoadDMABlocks(0x%08x, 0x%08x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
||||
uint32 numBlocks = hCPU->gpr[5];
|
||||
if (numBlocks == 0)
|
||||
numBlocks = 128;
|
||||
uint32 transferSize = numBlocks * 32;
|
||||
uint8* destPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
uint8* srcPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
|
||||
// copy right away, we don't emulate the DMAQueue currently
|
||||
memcpy(destPtr, srcPtr, transferSize);
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_LCStoreDMABlocks(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
//printf("LCStoreDMABlocks(0x%08x, 0x%08x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
||||
uint32 numBlocks = hCPU->gpr[5];
|
||||
if (numBlocks == 0)
|
||||
numBlocks = 128;
|
||||
//uint32 transferSize = numBlocks*32;
|
||||
uint8* destPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
uint8* srcPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
|
||||
// copy right away, we don't emulate the DMAQueue currently
|
||||
memcpy_qwords(destPtr, srcPtr, numBlocks * (32 / sizeof(uint64)));
|
||||
|
||||
LatteBufferCache_notifyDCFlush(hCPU->gpr[3], numBlocks * 32);
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_LCWaitDMAQueue(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
//printf("LCWaitDMAQueue(%d)\n", hCPU->gpr[3]);
|
||||
uint32 len = hCPU->gpr[3];
|
||||
// todo: Implement (be aware DMA queue is per core)
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void InitializeLC()
|
||||
{
|
||||
for (sint32 f = 0; f < PPC_CORE_COUNT; f++)
|
||||
{
|
||||
memset(lcCacheMask[f], LC_MASK_FREE, (LC_LOCKED_CACHE_SIZE + LC_LOCKED_CACHE_GRANULARITY - 1) / LC_LOCKED_CACHE_GRANULARITY);
|
||||
}
|
||||
|
||||
osLib_addFunction("coreinit", "LCGetMaxSize", coreinitExport_LCGetMaxSize);
|
||||
|
||||
osLib_addFunction("coreinit", "LCAlloc", coreinitExport_LCAlloc);
|
||||
osLib_addFunction("coreinit", "LCDealloc", coreinitExport_LCDealloc);
|
||||
osLib_addFunction("coreinit", "LCGetUnallocated", coreinitExport_LCGetUnallocated);
|
||||
osLib_addFunction("coreinit", "LCGetAllocatableSize", coreinitExport_LCGetAllocatableSize);
|
||||
|
||||
osLib_addFunction("coreinit", "LCEnableDMA", coreinitExport_LCEnableDMA);
|
||||
osLib_addFunction("coreinit", "LCDisableDMA", coreinitExport_LCDisableDMA);
|
||||
osLib_addFunction("coreinit", "LCIsDMAEnabled", coreinitExport_LCIsDMAEnabled);
|
||||
osLib_addFunction("coreinit", "LCHardwareIsAvailable", coreinitExport_LCHardwareIsAvailable);
|
||||
|
||||
osLib_addFunction("coreinit", "LCLoadDMABlocks", coreinitExport_LCLoadDMABlocks);
|
||||
osLib_addFunction("coreinit", "LCStoreDMABlocks", coreinitExport_LCStoreDMABlocks);
|
||||
osLib_addFunction("coreinit", "LCWaitDMAQueue", coreinitExport_LCWaitDMAQueue);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
6
src/Cafe/OS/libs/coreinit/coreinit_LockedCache.h
Normal file
6
src/Cafe/OS/libs/coreinit/coreinit_LockedCache.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitializeLC();
|
||||
}
|
||||
561
src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp
Normal file
561
src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/IOSU/legacy/iosu_ioctl.h"
|
||||
#include "Cafe/IOSU/legacy/iosu_mcp.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_IOS.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MCP.h"
|
||||
|
||||
|
||||
#include "Cafe/OS/libs/nn_common.h"
|
||||
#include "Cafe/OS/libs/nn_act/nn_act.h"
|
||||
#include "config/CemuConfig.h"
|
||||
|
||||
#include "Cafe/CafeSystem.h"
|
||||
|
||||
#define mcpPrepareRequest() \
|
||||
StackAllocator<iosuMcpCemuRequest_t> _buf_mcpRequest; \
|
||||
StackAllocator<ioBufferVector_t> _buf_bufferVector; \
|
||||
iosuMcpCemuRequest_t* mcpRequest = _buf_mcpRequest.GetPointer(); \
|
||||
ioBufferVector_t* mcpBufferVector = _buf_bufferVector.GetPointer(); \
|
||||
memset(mcpRequest, 0, sizeof(iosuMcpCemuRequest_t)); \
|
||||
memset(mcpBufferVector, 0, sizeof(ioBufferVector_t)); \
|
||||
mcpBufferVector->buffer = (uint8*)mcpRequest;
|
||||
|
||||
#define MCP_FAKE_HANDLE (0x00000010)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32be n0;
|
||||
uint32be n1;
|
||||
uint32be n2;
|
||||
}mcpSystemVersion_t;
|
||||
|
||||
static_assert(sizeof(MCPTitleInfo) == 0x61, "title entry size is invalid");
|
||||
static_assert(offsetof(MCPTitleInfo, appPath) == 0xC, "title entry appPath has invalid offset");
|
||||
static_assert(offsetof(MCPTitleInfo, titleVersion) == 0x48, "title entry titleVersion has invalid offset");
|
||||
static_assert(offsetof(MCPTitleInfo, osVersion) == 0x4A, "title entry osVersion has invalid offset");
|
||||
|
||||
MCPHANDLE MCP_Open()
|
||||
{
|
||||
// placeholder
|
||||
return MCP_FAKE_HANDLE;
|
||||
}
|
||||
|
||||
void MCP_Close(MCPHANDLE mcpHandle)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
void coreinitExport_MCP_Open(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// placeholder
|
||||
osLib_returnFromFunction(hCPU, MCP_Open());
|
||||
}
|
||||
|
||||
void coreinitExport_MCP_Close(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// placeholder
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
sint32 MCP_GetSysProdSettings(MCPHANDLE mcpHandle, SysProdSettings* sysProdSettings)
|
||||
{
|
||||
memset(sysProdSettings, 0x00, sizeof(SysProdSettings));
|
||||
// todo: Other fields are currently unknown
|
||||
|
||||
sysProdSettings->gameRegion = (uint8)CafeSystem::GetForegroundTitleRegion();
|
||||
sysProdSettings->platformRegion = (uint8)CafeSystem::GetPlatformRegion();
|
||||
|
||||
// contains Wii U serial parts at +0x1A and +0x22?
|
||||
return 0;
|
||||
}
|
||||
|
||||
void coreinitExport_MCP_GetSysProdSettings(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("MCP_GetSysProdSettings(0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
sint32 result = MCP_GetSysProdSettings(hCPU->gpr[3], (SysProdSettings*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]));
|
||||
osLib_returnFromFunction(hCPU, result);
|
||||
}
|
||||
|
||||
void coreinitExport_MCP_TitleListByAppType(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("MCP_TitleListByAppType(0x%08x,0x%08x,0x%08x,0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]);
|
||||
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamU32(appType, 1);
|
||||
ppcDefineParamU32BEPtr(countOutput, 2);
|
||||
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 3);
|
||||
ppcDefineParamU32(titleListSize, 4);
|
||||
|
||||
sint32 maxCount = titleListSize / sizeof(MCPTitleInfo);
|
||||
|
||||
mcpPrepareRequest();
|
||||
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST_BY_APP_TYPE;
|
||||
mcpRequest->titleListRequest.titleCount = maxCount;
|
||||
mcpRequest->titleListRequest.titleList = titleList;
|
||||
mcpRequest->titleListRequest.titleListBufferSize = sizeof(MCPTitleInfo) * 1;
|
||||
mcpRequest->titleListRequest.appType = appType;
|
||||
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
|
||||
|
||||
*countOutput = mcpRequest->titleListRequest.titleCount;
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_MCP_TitleList(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("MCP_TitleList(...) unimplemented");
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamU32BEPtr(countOutput, 1);
|
||||
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 2);
|
||||
ppcDefineParamU32(titleListBufferSize, 3);
|
||||
|
||||
// todo -> Other parameters
|
||||
|
||||
mcpPrepareRequest();
|
||||
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST;
|
||||
mcpRequest->titleListRequest.titleCount = *countOutput;
|
||||
mcpRequest->titleListRequest.titleList = titleList;
|
||||
mcpRequest->titleListRequest.titleListBufferSize = titleListBufferSize;
|
||||
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
|
||||
|
||||
*countOutput = mcpRequest->titleListRequest.titleCount;
|
||||
|
||||
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
|
||||
}
|
||||
|
||||
void coreinitExport_MCP_TitleCount(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("MCP_TitleCount(): Untested");
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
|
||||
mcpPrepareRequest();
|
||||
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_COUNT;
|
||||
mcpRequest->titleListRequest.titleCount = 0;
|
||||
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
|
||||
|
||||
osLib_returnFromFunction(hCPU, mcpRequest->titleListRequest.titleCount);
|
||||
}
|
||||
|
||||
void coreinitExport_MCP_GetTitleInfo(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamU64(titleId, 2);
|
||||
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 4);
|
||||
|
||||
forceLogDebug_printf("MCP_GetTitleInfo() called");
|
||||
|
||||
mcpPrepareRequest();
|
||||
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_INFO;
|
||||
mcpRequest->titleListRequest.titleCount = 1;
|
||||
mcpRequest->titleListRequest.titleList = titleList;
|
||||
mcpRequest->titleListRequest.titleListBufferSize = sizeof(MCPTitleInfo);
|
||||
mcpRequest->titleListRequest.titleId = titleId;
|
||||
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
|
||||
|
||||
if (mcpRequest->titleListRequest.titleCount == 0)
|
||||
{
|
||||
forceLog_printf("MCP_GetTitleInfo() failed to get title info");
|
||||
}
|
||||
|
||||
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
|
||||
|
||||
}
|
||||
|
||||
void coreinitExport_MCP_GetTitleInfoByTitleAndDevice(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamU64(titleId, 2);
|
||||
ppcDefineParamStr(device, 4); // e.g. "odd"
|
||||
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 5);
|
||||
|
||||
forceLogDebug_printf("MCP_GetTitleInfoByTitleAndDevice() called (todo - device type support)");
|
||||
|
||||
mcpPrepareRequest();
|
||||
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_INFO;
|
||||
mcpRequest->titleListRequest.titleCount = 1;
|
||||
mcpRequest->titleListRequest.titleList = titleList;
|
||||
mcpRequest->titleListRequest.titleListBufferSize = sizeof(MCPTitleInfo);
|
||||
mcpRequest->titleListRequest.titleId = titleId;
|
||||
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
|
||||
|
||||
if (mcpRequest->titleListRequest.titleCount == 0)
|
||||
{
|
||||
forceLogDebug_printf("MCP_GetTitleInfoByTitleAndDevice() no title found");
|
||||
osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_MCP, 0)); // E-Shop/nn_vctl.rpl expects error to be returned when no title is found
|
||||
return;
|
||||
}
|
||||
|
||||
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
|
||||
|
||||
}
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
void export_MCP_GetSystemVersion(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("MCP_GetSystemVersion(%d,0x%08x)", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamStructPtr(systemVersion, mcpSystemVersion_t, 1);
|
||||
|
||||
systemVersion->n0 = 0x5;
|
||||
systemVersion->n1 = 0x5;
|
||||
systemVersion->n2 = 0x2;
|
||||
// todo: Load this from \sys\title\00050010\10041200\content\version.bin
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_MCP_Get4SecondOffStatus(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// r3 = mcpHandle
|
||||
forceLogDebug_printf("MCP_Get4SecondOffStatus(...) placeholder");
|
||||
// if this returns 1 then Barista will display the warning about cold-shutdown ('Holding the POWER button for at least four seconds...')
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_MCP_TitleListByDevice(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamStr(deviceName, 1);
|
||||
ppcDefineParamU32BEPtr(titleCount, 2);
|
||||
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 3); // type guessed
|
||||
// purpose of r7 parameter is unknown
|
||||
|
||||
cemu_assert_unimplemented();
|
||||
|
||||
titleList[0].titleIdHigh = 0x00050000;
|
||||
titleList[0].titleIdLow = 0x11223344;
|
||||
strcpy(titleList[0].appPath, "titlePathTest");
|
||||
|
||||
*titleCount = 1;
|
||||
|
||||
forceLogDebug_printf("MCP_TitleListByDevice() - placeholder");
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct
|
||||
{
|
||||
/* +0x000 */ char storageName[0x90]; // the name in the storage path
|
||||
/* +0x090 */ char storagePath[0x280 - 1]; // /vol/storage_%s%02x
|
||||
/* +0x30F */ uint32be storageSubindexOrMask; // the id in the storage path, but this might also be a MASK of indices (e.g. 1 -> Only device 1, 7 -> Device 1,2,3) men.rpx expects 0xF (or 0x7?) to be set for MLC, SLC and USB for MLC_FullDeviceList
|
||||
uint8 ukn313[4];
|
||||
uint8 ukn317[4];
|
||||
}MCPDevice_t;
|
||||
#pragma pack()
|
||||
|
||||
static_assert(sizeof(MCPDevice_t) == 0x31B, "MCPDevice_t has invalid size");
|
||||
static_assert(offsetof(MCPDevice_t, storagePath) == 0x090, "MCPDevice_t.storagePath has invalid offset");
|
||||
static_assert(offsetof(MCPDevice_t, storageSubindexOrMask) == 0x30F, "MCPDevice_t.storageSubindex has invalid offset");
|
||||
static_assert(offsetof(MCPDevice_t, ukn313) == 0x313, "MCPDevice_t.ukn313 has invalid offset");
|
||||
static_assert(offsetof(MCPDevice_t, ukn317) == 0x317, "MCPDevice_t.ukn317 has invalid offset");
|
||||
|
||||
void MCP_DeviceListEx(uint32 mcpHandle, uint32be* deviceCount, MCPDevice_t* deviceList, uint32 deviceListSize, bool returnFullList)
|
||||
{
|
||||
sint32 maxDeviceCount = deviceListSize / sizeof(MCPDevice_t);
|
||||
|
||||
if (maxDeviceCount < 3*3)
|
||||
assert_dbg();
|
||||
|
||||
// if this doesnt return both MLC and SLC friendlist (frd.rpx) will softlock during boot
|
||||
|
||||
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
|
||||
sint32 index = 0;
|
||||
for (sint32 f = 0; f < 1; f++)
|
||||
{
|
||||
// 0
|
||||
strcpy(deviceList[index].storageName, "mlc");
|
||||
deviceList[index].storageSubindexOrMask = 0xF; // bitmask?
|
||||
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
|
||||
index++;
|
||||
// 1
|
||||
strcpy(deviceList[index].storageName, "slc");
|
||||
deviceList[index].storageSubindexOrMask = 0xF; // bitmask?
|
||||
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
|
||||
index++;
|
||||
// 2
|
||||
strcpy(deviceList[index].storageName, "usb");
|
||||
deviceList[index].storageSubindexOrMask = 0xF;
|
||||
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
|
||||
index++;
|
||||
}
|
||||
|
||||
|
||||
*deviceCount = index;
|
||||
}
|
||||
|
||||
|
||||
void export_MCP_DeviceList(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamU32BEPtr(deviceCount, 1);
|
||||
ppcDefineParamStructPtr(deviceList, MCPDevice_t, 2);
|
||||
ppcDefineParamU32(deviceListSize, 3);
|
||||
|
||||
forceLogDebug_printf("MCP_DeviceList() - placeholder LR %08x", hCPU->spr.LR);
|
||||
|
||||
sint32 maxDeviceCount = deviceListSize / sizeof(MCPDevice_t);
|
||||
|
||||
if (maxDeviceCount < 2)
|
||||
assert_dbg();
|
||||
|
||||
// if this doesnt return both MLC and SLC friendlist (frd.rpx) will softlock during boot
|
||||
|
||||
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
|
||||
// 0
|
||||
strcpy(deviceList[0].storageName, "mlc");
|
||||
deviceList[0].storageSubindexOrMask = (0x01); // bitmask?
|
||||
sprintf(deviceList[0].storagePath, "/vol/storage_%s%02x", deviceList[0].storageName, (sint32)deviceList[0].storageSubindexOrMask);
|
||||
// 1
|
||||
strcpy(deviceList[1].storageName, "slc");
|
||||
deviceList[1].storageSubindexOrMask = (0x01); // bitmask?
|
||||
sprintf(deviceList[1].storagePath, "/vol/storage_%s%02x", deviceList[1].storageName, (sint32)deviceList[1].storageSubindexOrMask);
|
||||
|
||||
// 2
|
||||
//strcpy(deviceList[2].storageName, "usb");
|
||||
//deviceList[2].storageSubindex = 1;
|
||||
//sprintf(deviceList[2].storagePath, "/vol/storage_%s%02x", deviceList[2].storageName, (sint32)deviceList[2].storageSubindex);
|
||||
|
||||
|
||||
*deviceCount = 2;
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_MCP_FullDeviceList(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamU32BEPtr(deviceCount, 1);
|
||||
ppcDefineParamStructPtr(deviceList, MCPDevice_t, 2);
|
||||
ppcDefineParamU32(deviceListSize, 3);
|
||||
|
||||
forceLogDebug_printf("MCP_FullDeviceList() - placeholder LR %08x", hCPU->spr.LR);
|
||||
|
||||
MCP_DeviceListEx(mcpHandle, deviceCount, deviceList, deviceListSize, true);
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_MCP_UpdateCheckContext(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamU32BEPtr(unknownParam, 1);
|
||||
|
||||
forceLogDebug_printf("MCP_UpdateCheckContext() - placeholder (might be wrong)");
|
||||
|
||||
*unknownParam = 1;
|
||||
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_MCP_TitleListUpdateGetNext(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamMPTR(callbackMPTR, 1);
|
||||
|
||||
forceLogDebug_printf("MCP_TitleListUpdateGetNext() - placeholder/unimplemented");
|
||||
|
||||
// this callback is to let the app know when the title list changed?
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_MCP_GetOverlayAppInfo(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
forceLogDebug_printf("MCP_GetOverlayAppInfo(...) placeholder");
|
||||
ppcDefineParamU32(mcpHandle, 0);
|
||||
ppcDefineParamU32(appType, 1);
|
||||
ppcDefineParamU32(uknR5, 2);
|
||||
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 3);
|
||||
|
||||
// hacky
|
||||
|
||||
mcpPrepareRequest();
|
||||
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST_BY_APP_TYPE;
|
||||
mcpRequest->titleListRequest.titleCount = 1;
|
||||
mcpRequest->titleListRequest.titleList = titleList;
|
||||
mcpRequest->titleListRequest.titleListBufferSize = sizeof(MCPTitleInfo)*1;
|
||||
mcpRequest->titleListRequest.appType = appType;
|
||||
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
|
||||
|
||||
if (mcpRequest->titleListRequest.titleCount != 1)
|
||||
assert_dbg();
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void InitializeMCP()
|
||||
{
|
||||
osLib_addFunction("coreinit", "MCP_Open", coreinitExport_MCP_Open);
|
||||
osLib_addFunction("coreinit", "MCP_Close", coreinitExport_MCP_Close);
|
||||
osLib_addFunction("coreinit", "MCP_GetSysProdSettings", coreinitExport_MCP_GetSysProdSettings);
|
||||
osLib_addFunction("coreinit", "MCP_TitleListByAppType", coreinitExport_MCP_TitleListByAppType);
|
||||
osLib_addFunction("coreinit", "MCP_TitleList", coreinitExport_MCP_TitleList);
|
||||
osLib_addFunction("coreinit", "MCP_TitleCount", coreinitExport_MCP_TitleCount);
|
||||
osLib_addFunction("coreinit", "MCP_GetTitleInfo", coreinitExport_MCP_GetTitleInfo);
|
||||
osLib_addFunction("coreinit", "MCP_GetTitleInfoByTitleAndDevice", coreinitExport_MCP_GetTitleInfoByTitleAndDevice);
|
||||
|
||||
osLib_addFunction("coreinit", "MCP_TitleListByDevice", export_MCP_TitleListByDevice);
|
||||
osLib_addFunction("coreinit", "MCP_GetSystemVersion", export_MCP_GetSystemVersion);
|
||||
osLib_addFunction("coreinit", "MCP_Get4SecondOffStatus", export_MCP_Get4SecondOffStatus);
|
||||
|
||||
osLib_addFunction("coreinit", "MCP_DeviceList", export_MCP_DeviceList);
|
||||
osLib_addFunction("coreinit", "MCP_FullDeviceList", export_MCP_FullDeviceList);
|
||||
|
||||
osLib_addFunction("coreinit", "MCP_UpdateCheckContext", export_MCP_UpdateCheckContext);
|
||||
osLib_addFunction("coreinit", "MCP_TitleListUpdateGetNext", export_MCP_TitleListUpdateGetNext);
|
||||
osLib_addFunction("coreinit", "MCP_GetOverlayAppInfo", export_MCP_GetOverlayAppInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char settingName[0x40];
|
||||
uint32 ukn1;
|
||||
uint32 ukn2;
|
||||
uint32 ukn3;
|
||||
uint32 ukn4_size; // size guessed
|
||||
MPTR resultPtr; // pointer to output value
|
||||
}UCParamStruct_t;
|
||||
|
||||
static_assert(sizeof(UCParamStruct_t) == 0x54); // unsure
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
#define _strcmpi strcasecmp
|
||||
#endif
|
||||
|
||||
void coreinitExport_UCReadSysConfig(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 = UC handle?
|
||||
// r4 = Number of values to read (count of UCParamStruct_t entries)
|
||||
// r5 = UCParamStruct_t*
|
||||
UCParamStruct_t* ucParamBase = (UCParamStruct_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[5]);
|
||||
uint32 ucParamCount = hCPU->gpr[4];
|
||||
for(uint32 i=0; i<ucParamCount; i++)
|
||||
{
|
||||
UCParamStruct_t* ucParam = ucParamBase+i;
|
||||
if(_strcmpi(ucParam->settingName, "cafe.cntry_reg") == 0)
|
||||
{
|
||||
// country code
|
||||
// get country from simple address
|
||||
uint32be simpleAddress = 0;
|
||||
nn::act::GetSimpleAddressIdEx(&simpleAddress, nn::act::ACT_SLOT_CURRENT);
|
||||
|
||||
uint32 countryCode = nn::act::getCountryCodeFromSimpleAddress(simpleAddress);
|
||||
|
||||
if( ucParam->resultPtr != _swapEndianU32(MPTR_NULL) )
|
||||
memory_writeU32(_swapEndianU32(ucParam->resultPtr), countryCode);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "cafe.language") == 0)
|
||||
{
|
||||
// language
|
||||
// 0 -> Japanese
|
||||
// 1 -> English
|
||||
// ...
|
||||
uint32 languageId = (uint32)GetConfig().console_language.GetValue();
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||
memory_writeU32(_swapEndianU32(ucParam->resultPtr), languageId);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "cafe.initial_launch") == 0)
|
||||
{
|
||||
// initial launch
|
||||
// possible ids:
|
||||
// 0xFF Set by SCIFormatSystem (default?)
|
||||
// 0x01 Inited but no user created yet (setting this will make the home menu go into user creation dialog)
|
||||
// 0x02 Inited, with user
|
||||
// other values are unknown
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 2);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "cafe.eula_version") == 0)
|
||||
{
|
||||
// todo - investigate values
|
||||
memory_writeU32(_swapEndianU32(ucParam->resultPtr), 0);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "cafe.eula_agree") == 0)
|
||||
{
|
||||
// todo - investigate values
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "cafe.version") == 0)
|
||||
{
|
||||
// todo - investigate values
|
||||
memory_writeU16(_swapEndianU32(ucParam->resultPtr), 0);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "cafe.eco") == 0)
|
||||
{
|
||||
// todo - investigate values
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "cafe.fast_boot") == 0)
|
||||
{
|
||||
// todo - investigate values
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "parent.enable") == 0)
|
||||
{
|
||||
// parental feature enabled
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||
memory_writeU32(_swapEndianU32(ucParam->resultPtr), 0);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "nn.act.account_repaired") == 0)
|
||||
{
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "p_acct1.net_communication_on_game") == 0)
|
||||
{
|
||||
// get parental online control for online features
|
||||
// note: This option is account-bound, the p_acct1 prefix indicates that the account in slot 1 is used
|
||||
// account in slot 1
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // data type is guessed
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "p_acct1.int_movie") == 0)
|
||||
{
|
||||
// get parental online control for movies?
|
||||
// used by Pikmin HD movies
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // 0 -> allowed
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "p_acct1.network_launcher") == 0)
|
||||
{
|
||||
// miiverse restrictions
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // data type is guessed (0 -> no restrictions, 1 -> read only?, 2 -> no access?)
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "s_acct01.uuid") == 0)
|
||||
{
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL) && ucParam->ukn4_size >= 37)
|
||||
{
|
||||
StackAllocator<uint8, 16> _uuid;
|
||||
uint8* uuid = _uuid.GetPointer();
|
||||
nn::act::GetUuidEx(uuid, 1, 0);
|
||||
char tempStr[64];
|
||||
sprintf(tempStr, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
|
||||
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
|
||||
strcpy((char*)memory_getPointerFromVirtualOffset(_swapEndianU32(ucParam->resultPtr)), tempStr);
|
||||
}
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "s_acct01.nn.ec.eshop_initialized") == 0)
|
||||
{
|
||||
// E-Shop welcome screen
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 1); // data type is guessed
|
||||
}
|
||||
else if (_strcmpi(ucParam->settingName, "p_acct1.int_browser") == 0)
|
||||
{
|
||||
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
forceLogDebug_printf("Unsupported SCI value: %s Size %08x\n", ucParam->settingName, ucParam->ukn4_size);
|
||||
}
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0); // 0 -> success
|
||||
}
|
||||
54
src/Cafe/OS/libs/coreinit/coreinit_MCP.h
Normal file
54
src/Cafe/OS/libs/coreinit/coreinit_MCP.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
struct SysProdSettings
|
||||
{
|
||||
uint8 ukn00; // 0x00
|
||||
uint8 ukn01; // 0x01
|
||||
uint8 ukn02; // 0x02
|
||||
uint8 platformRegion; // 0x03
|
||||
uint8 ukn04;
|
||||
uint8 ukn05;
|
||||
uint8 ukn06;
|
||||
uint8 ukn07;
|
||||
uint8 prevBlock;
|
||||
uint8 ukn09;
|
||||
uint8 ukn0A;
|
||||
uint8 gameRegion; // game region?
|
||||
uint8 nextBlock;
|
||||
uint8 ukn0D;
|
||||
uint8 ukn0E;
|
||||
uint8 ukn0F;
|
||||
uint8 ukn10;
|
||||
uint8 ukn11;
|
||||
uint8 ukn12;
|
||||
uint8 ukn13;
|
||||
uint8 ukn14[4];
|
||||
uint8 ukn18[4];
|
||||
uint8 ukn1C[4];
|
||||
uint8 ukn20[4];
|
||||
uint8 ukn24[4];
|
||||
uint8 ukn28[4];
|
||||
uint8 ukn2C[4];
|
||||
uint8 ukn30[4];
|
||||
uint8 ukn34[4];
|
||||
uint8 ukn38[4];
|
||||
uint8 ukn3C[4];
|
||||
uint8 ukn40[4];
|
||||
uint8 ukn44;
|
||||
uint8 ukn45;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SysProdSettings) == 0x46);
|
||||
|
||||
typedef uint32 MCPHANDLE;
|
||||
|
||||
MCPHANDLE MCP_Open();
|
||||
void MCP_Close(MCPHANDLE mcpHandle);
|
||||
sint32 MCP_GetSysProdSettings(MCPHANDLE mcpHandle, SysProdSettings* sysProdSettings);
|
||||
|
||||
void coreinitExport_UCReadSysConfig(PPCInterpreter_t* hCPU);
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitializeMCP();
|
||||
}
|
||||
651
src/Cafe/OS/libs/coreinit/coreinit_MEM.cpp
Normal file
651
src/Cafe/OS/libs/coreinit/coreinit_MEM.cpp
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Memory.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.h"
|
||||
#include "coreinit_DynLoad.h"
|
||||
|
||||
// the system area is a block of memory that exists only in the emulator. It is used to simplify dynamic memory allocation for system data
|
||||
// this partially overlaps with the system heap from coreinit_SysHeap.cpp -> Use SysHeap for everything
|
||||
MPTR sysAreaAllocatorOffset = 0;
|
||||
|
||||
MPTR coreinit_allocFromSysArea(uint32 size, uint32 alignment)
|
||||
{
|
||||
cemu_assert_debug(mmuRange_CEMU_AREA.isMapped());
|
||||
// deprecated, use OSAllocFromSystem instead (for everything but SysAllocator which probably should use its own allocator?)
|
||||
static std::mutex s_allocator_mutex;
|
||||
s_allocator_mutex.lock();
|
||||
sysAreaAllocatorOffset = (sysAreaAllocatorOffset+alignment-1);
|
||||
sysAreaAllocatorOffset -= (sysAreaAllocatorOffset%alignment);
|
||||
uint32 newMemOffset = mmuRange_CEMU_AREA.getBase() + sysAreaAllocatorOffset;
|
||||
sysAreaAllocatorOffset += (size+3)&~3;
|
||||
if( sysAreaAllocatorOffset >= mmuRange_CEMU_AREA.getSize() )
|
||||
{
|
||||
forceLog_printf("Ran out of system memory");
|
||||
cemu_assert(false); // out of bounds
|
||||
}
|
||||
s_allocator_mutex.unlock();
|
||||
return newMemOffset;
|
||||
}
|
||||
|
||||
void coreinit_freeToSysArea(MPTR mem)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
#define MEM_MAX_HEAP_TABLE (0x20)
|
||||
|
||||
sint32 g_heapTableCount = 0;
|
||||
MEMHeapBase* g_heapTable[MEM_MAX_HEAP_TABLE] = {};
|
||||
|
||||
bool g_slockInitialized = false;
|
||||
bool g_listsInitialized = false;
|
||||
|
||||
MEMList g_list1;
|
||||
MEMList g_list2;
|
||||
MEMList g_list3;
|
||||
|
||||
std::array<uint32, 3> gHeapFillValues{ 0xC3C3C3C3, 0xF3F3F3F3, 0xD3D3D3D3 };
|
||||
OSSpinLock gHeapGlobalLock;
|
||||
MEMHeapBase* gDefaultHeap;
|
||||
|
||||
bool MEMHeapTable_Add(MEMHeapBase* heap)
|
||||
{
|
||||
if (g_heapTableCount >= MEM_MAX_HEAP_TABLE)
|
||||
return false;
|
||||
g_heapTable[g_heapTableCount] = heap;
|
||||
g_heapTableCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MEMHeapTable_Remove(MEMHeapBase* heap)
|
||||
{
|
||||
if (g_heapTableCount == 0)
|
||||
return false;
|
||||
|
||||
if (g_heapTableCount > MEM_MAX_HEAP_TABLE)
|
||||
return false;
|
||||
|
||||
for (sint32 i = 0; i < g_heapTableCount; ++i)
|
||||
{
|
||||
if (g_heapTable[i] == heap)
|
||||
{
|
||||
g_heapTable[i] = nullptr;
|
||||
g_heapTableCount--;
|
||||
|
||||
if (g_heapTableCount == 0)
|
||||
return true;
|
||||
|
||||
if (i >= g_heapTableCount)
|
||||
return true;
|
||||
|
||||
cemu_assert_debug(i < MEM_MAX_HEAP_TABLE);
|
||||
for (; i < g_heapTableCount; ++i)
|
||||
{
|
||||
g_heapTable[i] = g_heapTable[i + 1];
|
||||
}
|
||||
|
||||
cemu_assert_debug(i < MEM_MAX_HEAP_TABLE);
|
||||
g_heapTable[i] = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MEMHeapBase* _MEMList_FindContainingHeap(MEMList* list, MEMHeapBase* heap)
|
||||
{
|
||||
for (MEMHeapBase* obj = (MEMHeapBase*)MEMGetFirstListObject(list); obj; obj = (MEMHeapBase*)MEMGetNextListObject(list, obj))
|
||||
{
|
||||
if (obj->heapStart.GetPtr() <= heap && heap < obj->heapEnd.GetPtr())
|
||||
{
|
||||
const MEMHeapHandle containHeap = _MEMList_FindContainingHeap(&obj->childList, heap);
|
||||
return containHeap ? containHeap : obj;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool MEMList_ContainsHeap(MEMList* list, MEMHeapBase* heap)
|
||||
{
|
||||
for (MEMHeapBase* obj = (MEMHeapBase*)MEMGetFirstListObject(list); obj; obj = (MEMHeapBase*)MEMGetNextListObject(list, obj))
|
||||
{
|
||||
if (obj == heap)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MEMList* MEMList_FindContainingHeap(MEMHeapBase* head)
|
||||
{
|
||||
MEMPTR<void> memBound;
|
||||
uint32be memBoundSize;
|
||||
OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
|
||||
|
||||
MEMPTR<void> bucket;
|
||||
uint32be bucketSize;
|
||||
coreinit::OSGetForegroundBucket(&bucket, &bucketSize);
|
||||
|
||||
if ((uintptr_t)memBound.GetPtr() > (uintptr_t)head || (uintptr_t)head >= (uintptr_t)memBound.GetPtr() + (uint32)memBoundSize)
|
||||
{
|
||||
if ((uintptr_t)bucket.GetPtr() > (uintptr_t)head || (uintptr_t)head >= (uintptr_t)bucket.GetPtr() + (uint32)bucketSize)
|
||||
{
|
||||
MEMHeapBase* containHeap = _MEMList_FindContainingHeap(&g_list1, head);
|
||||
if (containHeap)
|
||||
return &containHeap->childList;
|
||||
|
||||
return &g_list1;
|
||||
}
|
||||
|
||||
MEMHeapBase* containHeap = _MEMList_FindContainingHeap(&g_list3, head);
|
||||
if (containHeap)
|
||||
return &containHeap->childList;
|
||||
|
||||
return &g_list3;
|
||||
}
|
||||
|
||||
MEMHeapBase* containHeap = _MEMList_FindContainingHeap(&g_list2, head);
|
||||
if (containHeap)
|
||||
return &containHeap->childList;
|
||||
|
||||
return &g_list2;
|
||||
}
|
||||
|
||||
void MEMInitHeapBase(MEMHeapBase* heap, MEMHeapMagic magic, void* heapStart, void* heapEnd, uint32 createFlags)
|
||||
{
|
||||
memset(heap, 0, sizeof(MEMHeapBase));
|
||||
heap->magic = magic;
|
||||
heap->heapStart = heapStart;
|
||||
heap->heapEnd = heapEnd;
|
||||
heap->flags = (uint8)createFlags;
|
||||
|
||||
MEMInitList(&heap->childList, 4);
|
||||
|
||||
if (!g_slockInitialized)
|
||||
{
|
||||
OSInitSpinLock(&gHeapGlobalLock);
|
||||
g_slockInitialized = true;
|
||||
}
|
||||
|
||||
if (!g_listsInitialized)
|
||||
{
|
||||
MEMInitList(&g_list1, offsetof(MEMHeapBase, link));
|
||||
MEMInitList(&g_list2, offsetof(MEMHeapBase, link));
|
||||
MEMInitList(&g_list3, offsetof(MEMHeapBase, link));
|
||||
g_listsInitialized = true;
|
||||
}
|
||||
|
||||
OSInitSpinLock(&heap->spinlock);
|
||||
|
||||
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
|
||||
|
||||
MEMList* list = MEMList_FindContainingHeap(heap);
|
||||
MEMAppendListObject(list, heap);
|
||||
|
||||
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
|
||||
}
|
||||
|
||||
void MEMBaseDestroyHeap(MEMHeapBase* heap)
|
||||
{
|
||||
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
|
||||
|
||||
if (HAS_FLAG(heap->flags, MEM_HEAP_OPTION_THREADSAFE))
|
||||
OSUninterruptibleSpinLock_Acquire(&heap->spinlock);
|
||||
|
||||
MEMList* containHeap = MEMList_FindContainingHeap(heap);
|
||||
cemu_assert_debug(MEMList_ContainsHeap(containHeap, heap));
|
||||
MEMRemoveListObject(containHeap, heap);
|
||||
|
||||
if (HAS_FLAG(heap->flags, MEM_HEAP_OPTION_THREADSAFE))
|
||||
OSUninterruptibleSpinLock_Release(&heap->spinlock);
|
||||
|
||||
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
|
||||
}
|
||||
|
||||
std::array<MEMHeapBase*, 9> sHeapBaseHandle{};
|
||||
|
||||
MEMHeapBase* MEMGetBaseHeapHandle(uint32 index)
|
||||
{
|
||||
if (index >= sHeapBaseHandle.size())
|
||||
return nullptr;
|
||||
return sHeapBaseHandle[index];
|
||||
}
|
||||
|
||||
MEMHeapBase* MEMSetBaseHeapHandle(uint32 index, MEMHeapBase* heapBase)
|
||||
{
|
||||
if (index >= sHeapBaseHandle.size())
|
||||
return nullptr;
|
||||
if (sHeapBaseHandle[index] != nullptr)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "MEMSetBaseHeapHandle(): Trying to assign heap to non-empty slot");
|
||||
return sHeapBaseHandle[index];
|
||||
}
|
||||
sHeapBaseHandle[index] = heapBase;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 MEMSetFillValForHeap(HEAP_FILL_TYPE type, uint32 value)
|
||||
{
|
||||
uint32 idx = (uint32)type;
|
||||
cemu_assert(idx < gHeapFillValues.size());
|
||||
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
|
||||
const uint32 oldValue = gHeapFillValues[idx];
|
||||
gHeapFillValues[idx] = value;
|
||||
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
uint32 MEMGetFillValForHeap(HEAP_FILL_TYPE type)
|
||||
{
|
||||
uint32 idx = (uint32)type;
|
||||
cemu_assert(idx < gHeapFillValues.size());
|
||||
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
|
||||
const uint32 value = gHeapFillValues[idx];
|
||||
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
|
||||
return value;
|
||||
}
|
||||
|
||||
MEMHeapBase* MEMFindContainHeap(const void* memBlock)
|
||||
{
|
||||
MEMPTR<void> memBound;
|
||||
uint32be memBoundSize;
|
||||
OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
|
||||
|
||||
MEMPTR<void> bucket;
|
||||
uint32be bucketSize;
|
||||
coreinit::OSGetForegroundBucket(&bucket, &bucketSize);
|
||||
|
||||
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
|
||||
|
||||
MEMHeapBase* result;
|
||||
if ((uintptr_t)memBound.GetPtr() > (uintptr_t)memBlock || (uintptr_t)memBlock >= (uintptr_t)memBound.GetPtr() + (uint32)memBoundSize)
|
||||
{
|
||||
if ((uintptr_t)bucket.GetPtr() > (uintptr_t)memBlock || (uintptr_t)memBlock >= (uintptr_t)bucket.GetPtr() + (uint32)bucketSize)
|
||||
{
|
||||
result = _MEMList_FindContainingHeap(&g_list1, (MEMHeapBase*)memBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (coreinit::OSGetForegroundBucket(nullptr, nullptr) == 0)
|
||||
{
|
||||
cemu_assert_unimplemented(); // foreground required
|
||||
result = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _MEMList_FindContainingHeap(&g_list3, (MEMHeapBase*)memBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (coreinit::OSGetForegroundBucket(nullptr, nullptr) == 0)
|
||||
{
|
||||
cemu_assert_unimplemented(); // foreground required
|
||||
result = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _MEMList_FindContainingHeap(&g_list2, (MEMHeapBase*)memBlock);
|
||||
}
|
||||
}
|
||||
|
||||
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void* MEMCreateUserHeapHandle(void* heapAddress, uint32 heapSize)
|
||||
{
|
||||
MEMInitHeapBase((MEMHeapBase*)heapAddress, MEMHeapMagic::USER_HEAP, (uint8*)heapAddress + 0x40, (uint8*)heapAddress + heapSize, 0);
|
||||
return heapAddress;
|
||||
}
|
||||
|
||||
/* MEM Heap list */
|
||||
|
||||
#define GET_MEM_LINK(__obj__,__offset__) ((MEMLink*)((uintptr_t)__obj__ + (uint16)__offset__))
|
||||
|
||||
void* MEMGetFirstListObject(MEMList* list)
|
||||
{
|
||||
return MEMGetNextListObject(list, nullptr);
|
||||
}
|
||||
|
||||
void MEMList_SetSingleObject(MEMList* list, void* object)
|
||||
{
|
||||
cemu_assert_debug(list != nullptr);
|
||||
cemu_assert_debug(object != nullptr);
|
||||
|
||||
MEMLink* link = GET_MEM_LINK(object, list->offset);
|
||||
link->prev = nullptr;
|
||||
link->next = nullptr;
|
||||
|
||||
list->numObjects = list->numObjects + 1;
|
||||
list->head = object;
|
||||
list->tail = object;
|
||||
}
|
||||
|
||||
void MEMInitList(MEMList* list, uint32 offset)
|
||||
{
|
||||
cemu_assert_debug(list != nullptr);
|
||||
cemu_assert(offset <= 0xFFFF);
|
||||
memset(list, 0x00, sizeof(MEMLink));
|
||||
list->offset = (uint16)offset;
|
||||
}
|
||||
|
||||
void MEMAppendListObject(MEMList* list, void* object)
|
||||
{
|
||||
cemu_assert_debug(list != nullptr);
|
||||
cemu_assert_debug(object != nullptr);
|
||||
|
||||
if (list->head)
|
||||
{
|
||||
list->numObjects = list->numObjects + 1;
|
||||
|
||||
MEMLink* link = GET_MEM_LINK(object, list->offset);
|
||||
link->prev = list->tail;
|
||||
link->next = nullptr;
|
||||
|
||||
MEMLink* tailLink = GET_MEM_LINK(list->tail.GetPtr(), list->offset);
|
||||
tailLink->next = object;
|
||||
|
||||
list->tail = object;
|
||||
}
|
||||
else
|
||||
MEMList_SetSingleObject(list, object);
|
||||
}
|
||||
|
||||
void MEMPrependListObject(MEMList* list, void* object)
|
||||
{
|
||||
cemu_assert_debug(list != nullptr);
|
||||
cemu_assert_debug(object != nullptr);
|
||||
|
||||
MEMPTR<void> headObject = list->head;
|
||||
if (headObject)
|
||||
{
|
||||
list->numObjects = list->numObjects + 1;
|
||||
|
||||
MEMLink* link = GET_MEM_LINK(object, list->offset);
|
||||
link->prev = nullptr;
|
||||
link->next = headObject;
|
||||
|
||||
MEMLink* headLink = GET_MEM_LINK(headObject.GetPtr(), list->offset);
|
||||
headLink->prev = object;
|
||||
|
||||
list->head = object;
|
||||
}
|
||||
else
|
||||
MEMList_SetSingleObject(list, object);
|
||||
}
|
||||
|
||||
void MEMRemoveListObject(MEMList* list, void* object)
|
||||
{
|
||||
cemu_assert_debug(list != nullptr);
|
||||
cemu_assert_debug(object != nullptr);
|
||||
|
||||
MEMLink* link = GET_MEM_LINK(object, list->offset);
|
||||
|
||||
MEMPTR<void> prevObject = link->prev;
|
||||
if (prevObject)
|
||||
{
|
||||
MEMLink* prevLink = GET_MEM_LINK(prevObject.GetPtr(), list->offset);
|
||||
prevLink->next = link->next;
|
||||
}
|
||||
else
|
||||
list->head = link->next;
|
||||
|
||||
MEMPTR<void> nextObject = link->next;
|
||||
if (nextObject)
|
||||
{
|
||||
MEMLink* nextLink = GET_MEM_LINK(nextObject.GetPtr(), list->offset);
|
||||
nextLink->prev = prevObject;
|
||||
}
|
||||
else
|
||||
list->tail = prevObject;
|
||||
|
||||
link->prev = nullptr;
|
||||
link->next = nullptr;
|
||||
|
||||
list->numObjects = list->numObjects - 1;
|
||||
}
|
||||
|
||||
void* MEMGetNextListObject(MEMList* list, void* object)
|
||||
{
|
||||
cemu_assert_debug(list != nullptr);
|
||||
|
||||
if (!object)
|
||||
return list->head.GetPtr();
|
||||
|
||||
MEMLink* result = GET_MEM_LINK(object, list->offset);
|
||||
return result->next.GetPtr();
|
||||
}
|
||||
|
||||
void* MEMGetPrevListObject(MEMList* list, void* object)
|
||||
{
|
||||
cemu_assert_debug(list != nullptr);
|
||||
|
||||
if (!object)
|
||||
return list->tail.GetPtr();
|
||||
|
||||
MEMLink* result = GET_MEM_LINK(object, list->offset);
|
||||
return result->prev.GetPtr();
|
||||
}
|
||||
|
||||
void* MEMGetNthListObject(MEMList* list, uint32 index)
|
||||
{
|
||||
cemu_assert_debug(list != nullptr);
|
||||
|
||||
void* result = MEMGetNextListObject(list, nullptr);
|
||||
for (uint32 i = 0; i != index; ++i)
|
||||
{
|
||||
result = MEMGetNextListObject(list, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Default allocators */
|
||||
|
||||
MEMHeapBase* MEMDefaultHeap_Init(void* mem, uint32 size)
|
||||
{
|
||||
MEMHeapBase* heap = MEMCreateExpHeapEx(mem, size, 4);
|
||||
gDefaultHeap = heap;
|
||||
cemu_assert_debug(heap);
|
||||
return heap;
|
||||
}
|
||||
|
||||
void* default_MEMAllocFromDefaultHeap(uint32 size)
|
||||
{
|
||||
void* mem = MEMAllocFromExpHeapEx(gDefaultHeap, size, 0x40);
|
||||
coreinitMemLog_printf("MEMAllocFromDefaultHeap(0x%08x) Result: 0x%08x", size, memory_getVirtualOffsetFromPointer(mem));
|
||||
return mem;
|
||||
}
|
||||
|
||||
void export_default_MEMAllocFromDefaultHeap(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(size, 0);
|
||||
MEMPTR<void> mem = default_MEMAllocFromDefaultHeap(size);
|
||||
osLib_returnFromFunction(hCPU, mem.GetMPTR());
|
||||
}
|
||||
|
||||
void* default_MEMAllocFromDefaultHeapEx(uint32 size, sint32 alignment)
|
||||
{
|
||||
void* mem = MEMAllocFromExpHeapEx(gDefaultHeap, size, alignment);
|
||||
coreinitMemLog_printf("MEMAllocFromDefaultHeap(0x%08x,%d) Result: 0x%08x", size, alignment, memory_getVirtualOffsetFromPointer(mem));
|
||||
return mem;
|
||||
}
|
||||
|
||||
void export_default_MEMAllocFromDefaultHeapEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(size, 0);
|
||||
ppcDefineParamS32(alignment, 1);
|
||||
MEMPTR<void> mem = default_MEMAllocFromDefaultHeapEx(size, alignment);
|
||||
osLib_returnFromFunction(hCPU, mem.GetMPTR());
|
||||
}
|
||||
|
||||
void default_MEMFreeToDefaultHeap(void* mem)
|
||||
{
|
||||
MEMFreeToExpHeap(gDefaultHeap, mem);
|
||||
}
|
||||
|
||||
void export_default_MEMFreeToDefaultHeap(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamMEMPTR(mem, void, 0);
|
||||
default_MEMFreeToDefaultHeap(mem.GetPtr());
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void default_DynLoadAlloc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamS32(size, 0);
|
||||
ppcDefineParamS32(alignment, 1);
|
||||
ppcDefineParamMEMPTR(memResultPtr, uint32be, 2);
|
||||
MPTR mem = PPCCoreCallback(gCoreinitData->MEMAllocFromDefaultHeapEx.GetMPTR(), size, alignment);
|
||||
*memResultPtr = mem;
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void default_DynLoadFree(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamMEMPTR(mem, void, 0);
|
||||
PPCCoreCallback(gCoreinitData->MEMFreeToDefaultHeap.GetMPTR(), mem);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void* _weak_MEMAllocFromDefaultHeapEx(uint32 size, sint32 alignment)
|
||||
{
|
||||
MEMPTR<void> r{ PPCCoreCallback(gCoreinitData->MEMAllocFromDefaultHeapEx.GetMPTR(), size, alignment) };
|
||||
return r.GetPtr();
|
||||
}
|
||||
|
||||
void* _weak_MEMAllocFromDefaultHeap(uint32 size)
|
||||
{
|
||||
MEMPTR<void> r{ PPCCoreCallback(gCoreinitData->MEMAllocFromDefaultHeap.GetMPTR(), size) };
|
||||
return r.GetPtr();
|
||||
}
|
||||
|
||||
void _weak_MEMFreeToDefaultHeap(void* ptr)
|
||||
{
|
||||
PPCCoreCallback(gCoreinitData->MEMFreeToDefaultHeap.GetMPTR(), ptr);
|
||||
}
|
||||
|
||||
void coreinitExport_MEMAllocFromAllocator(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("MEMAllocFromAllocator(0x%x, 0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
MEMAllocator* memAllocator = (MEMAllocator*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
// redirect execution to allocator alloc callback
|
||||
hCPU->instructionPointer = memAllocator->func->funcAlloc.GetMPTR();
|
||||
}
|
||||
|
||||
void coreinitExport_MEMFreeToAllocator(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("MEMFreeToAllocator(0x%x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
MEMAllocator* memAllocator = (MEMAllocator*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
// redirect execution to allocator free callback
|
||||
hCPU->instructionPointer = memAllocator->func->funcFree.GetMPTR();
|
||||
}
|
||||
|
||||
void _DefaultHeapAllocator_Alloc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
hCPU->gpr[3] = hCPU->gpr[4];
|
||||
hCPU->instructionPointer = gCoreinitData->MEMAllocFromDefaultHeap.GetMPTR();
|
||||
}
|
||||
|
||||
void _DefaultHeapAllocator_Free(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
hCPU->gpr[3] = hCPU->gpr[4];
|
||||
hCPU->instructionPointer = gCoreinitData->MEMFreeToDefaultHeap.GetMPTR();
|
||||
}
|
||||
|
||||
SysAllocator<MEMAllocatorFunc> gDefaultHeapAllocator;
|
||||
|
||||
void coreinitExport_MEMInitAllocatorForDefaultHeap(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("MEMInitAllocatorForDefaultHeap(0x%08x)", hCPU->gpr[3]);
|
||||
ppcDefineParamStructPtr(memAllocator, MEMAllocator, 0);
|
||||
|
||||
gDefaultHeapAllocator->funcAlloc = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(_DefaultHeapAllocator_Alloc));
|
||||
gDefaultHeapAllocator->funcFree = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(_DefaultHeapAllocator_Free));
|
||||
|
||||
memAllocator->func = gDefaultHeapAllocator.GetPtr();
|
||||
memAllocator->heap = MEMPTR<void>(MEMGetBaseHeapHandle(1));
|
||||
memAllocator->param1 = 0;
|
||||
memAllocator->param2 = 0;
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void InitDefaultHeaps(MEMPTR<MEMHeapBase>& mem1Heap, MEMPTR<MEMHeapBase>& memFGHeap, MEMPTR<MEMHeapBase>& mem2Heap)
|
||||
{
|
||||
mem1Heap = nullptr;
|
||||
memFGHeap = nullptr;
|
||||
mem2Heap = nullptr;
|
||||
|
||||
gCoreinitData->MEMAllocFromDefaultHeap = RPLLoader_MakePPCCallable(export_default_MEMAllocFromDefaultHeap);
|
||||
gCoreinitData->MEMAllocFromDefaultHeapEx = RPLLoader_MakePPCCallable(export_default_MEMAllocFromDefaultHeapEx);
|
||||
gCoreinitData->MEMFreeToDefaultHeap = RPLLoader_MakePPCCallable(export_default_MEMFreeToDefaultHeap);
|
||||
|
||||
if (OSGetForegroundBucket(nullptr, nullptr))
|
||||
{
|
||||
MEMPTR<void> memBound;
|
||||
uint32be memBoundSize;
|
||||
OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
|
||||
mem1Heap = MEMCreateFrmHeapEx(memBound.GetPtr(), (uint32)memBoundSize, 0);
|
||||
|
||||
OSGetForegroundBucketFreeArea((MPTR*)memBound.GetBEPtr(), (MPTR*)&memBoundSize);
|
||||
memFGHeap = MEMCreateFrmHeapEx(memBound.GetPtr(), (uint32)memBoundSize, 0);
|
||||
}
|
||||
|
||||
MEMPTR<void> memBound;
|
||||
uint32be memBoundSize;
|
||||
OSGetMemBound(2, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
|
||||
mem2Heap = MEMDefaultHeap_Init(memBound.GetPtr(), (uint32)memBoundSize);
|
||||
|
||||
// set DynLoad allocators
|
||||
OSDynLoad_SetAllocator(RPLLoader_MakePPCCallable(default_DynLoadAlloc), RPLLoader_MakePPCCallable(default_DynLoadFree));
|
||||
OSDynLoad_SetTLSAllocator(RPLLoader_MakePPCCallable(default_DynLoadAlloc), RPLLoader_MakePPCCallable(default_DynLoadFree));
|
||||
}
|
||||
|
||||
void CoreInitDefaultHeap(/* ukn */)
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
void InitializeMEM()
|
||||
{
|
||||
for (auto& it : sHeapBaseHandle)
|
||||
it = nullptr;
|
||||
|
||||
cafeExportRegister("coreinit", CoreInitDefaultHeap, LogType::CoreinitMem);
|
||||
|
||||
osLib_addFunction("coreinit", "MEMInitAllocatorForDefaultHeap", coreinitExport_MEMInitAllocatorForDefaultHeap);
|
||||
osLib_addFunction("coreinit", "MEMAllocFromAllocator", coreinitExport_MEMAllocFromAllocator);
|
||||
osLib_addFunction("coreinit", "MEMFreeToAllocator", coreinitExport_MEMFreeToAllocator);
|
||||
|
||||
cafeExportRegister("coreinit", MEMGetBaseHeapHandle, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMSetBaseHeapHandle, LogType::CoreinitMem);
|
||||
|
||||
cafeExportRegister("coreinit", MEMFindContainHeap, LogType::CoreinitMem);
|
||||
|
||||
cafeExportRegister("coreinit", MEMGetFillValForHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMSetFillValForHeap, LogType::CoreinitMem);
|
||||
|
||||
cafeExportRegister("coreinit", MEMCreateUserHeapHandle, LogType::CoreinitMem);
|
||||
|
||||
cafeExportRegister("coreinit", MEMInitList, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMPrependListObject, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMAppendListObject, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMRemoveListObject, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMGetNextListObject, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMGetNthListObject, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMGetPrevListObject, LogType::CoreinitMem);
|
||||
}
|
||||
|
||||
}
|
||||
183
src/Cafe/OS/libs/coreinit/coreinit_MEM.h
Normal file
183
src/Cafe/OS/libs/coreinit/coreinit_MEM.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
#pragma once
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
|
||||
|
||||
struct MEMLink_t
|
||||
{
|
||||
MPTR prevObject;
|
||||
MPTR nextObject;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MEMLink_t) == 8);
|
||||
|
||||
struct MEMList_t
|
||||
{
|
||||
MPTR headObject;
|
||||
MPTR tailObject;
|
||||
uint16 numObjects;
|
||||
uint16 offset;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MEMList_t) == 0xC);
|
||||
|
||||
struct MEMAllocatorFunc
|
||||
{
|
||||
MEMPTR<void> funcAlloc;
|
||||
MEMPTR<void> funcFree;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MEMAllocatorFunc) == 8);
|
||||
|
||||
struct MEMAllocator
|
||||
{
|
||||
/* +0x000 */ MEMPTR<MEMAllocatorFunc> func;
|
||||
/* +0x004 */ MEMPTR<void> heap;
|
||||
/* +0x00C */ uint32be param1;
|
||||
/* +0x010 */ uint32be param2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MEMAllocator) == 0x10);
|
||||
|
||||
MPTR coreinit_allocFromSysArea(uint32 size, uint32 alignment);
|
||||
void coreinit_freeToSysArea(MPTR mem);
|
||||
|
||||
// mem exports
|
||||
void coreinitExport_MEMInitAllocatorForDefaultHeap(PPCInterpreter_t* hCPU);
|
||||
void coreinitExport_MEMGetBaseHeapHandle(PPCInterpreter_t* hCPU);
|
||||
|
||||
/* legacy stuff above */
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
#define MEM_HEAP_INVALID_HANDLE (nullptr)
|
||||
#define MEM_HEAP_DEFAULT_ALIGNMENT 4
|
||||
#define MIN_ALIGNMENT 4
|
||||
#define MIN_ALIGNMENT_MINUS_ONE (MIN_ALIGNMENT-1)
|
||||
|
||||
#define MEM_HEAP_OPTION_NONE (0)
|
||||
#define MEM_HEAP_OPTION_CLEAR (1 << 0)
|
||||
#define MEM_HEAP_OPTION_FILL (1 << 1)
|
||||
#define MEM_HEAP_OPTION_THREADSAFE (1 << 2)
|
||||
|
||||
enum class MEMHeapMagic : uint32
|
||||
{
|
||||
UNIT_HEAP = 'UNTH',
|
||||
BLOCK_HEAP = 'BLKH',
|
||||
FRAME_HEAP = 'FRMH',
|
||||
EXP_HEAP = 'EXPH',
|
||||
USER_HEAP = 'USRH',
|
||||
};
|
||||
|
||||
struct MEMLink
|
||||
{
|
||||
MEMPTR<void> prev;
|
||||
MEMPTR<void> next;
|
||||
};
|
||||
static_assert(sizeof(MEMLink) == 0x8);
|
||||
|
||||
struct MEMList
|
||||
{
|
||||
/* 0x00 */ MEMPTR<void> head;
|
||||
/* 0x04 */ MEMPTR<void> tail;
|
||||
/* 0x08 */ uint16be numObjects;
|
||||
/* 0x0A */ uint16be offset;
|
||||
};
|
||||
static_assert(sizeof(MEMList) == 0xC);
|
||||
|
||||
void MEMInitList(MEMList* list, uint32 offset);
|
||||
void MEMAppendListObject(MEMList* list, void* object);
|
||||
void MEMRemoveListObject(MEMList* list, void* object);
|
||||
|
||||
void* MEMGetFirstListObject(MEMList* list);
|
||||
void* MEMGetNextListObject(MEMList* list, void* object);
|
||||
|
||||
struct MEMHeapBase
|
||||
{
|
||||
/* +0x00 */ betype<MEMHeapMagic> magic;
|
||||
/* +0x04 */ MEMLink link;
|
||||
/* +0x0C */ MEMList childList;
|
||||
/* +0x18 */ MEMPTR<void> heapStart;
|
||||
/* +0x1C */ MEMPTR<void> heapEnd; // heap end + 1
|
||||
/* +0x20 */ OSSpinLock spinlock;
|
||||
/* +0x30 */ uint8 _ukn[3];
|
||||
/* +0x33 */ uint8 flags;
|
||||
|
||||
void AcquireLock()
|
||||
{
|
||||
if (flags & MEM_HEAP_OPTION_THREADSAFE)
|
||||
OSUninterruptibleSpinLock_Acquire(&spinlock);
|
||||
}
|
||||
|
||||
void ReleaseLock()
|
||||
{
|
||||
if (flags & MEM_HEAP_OPTION_THREADSAFE)
|
||||
OSUninterruptibleSpinLock_Release(&spinlock);
|
||||
}
|
||||
|
||||
// if set, memset allocations to zero
|
||||
bool HasOptionClear() const
|
||||
{
|
||||
return (flags & MEM_HEAP_OPTION_CLEAR) != 0;
|
||||
}
|
||||
|
||||
// if set, memset allocations/releases to specific fill values
|
||||
bool HasOptionFill() const
|
||||
{
|
||||
return (flags & MEM_HEAP_OPTION_FILL) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(offsetof(MEMHeapBase, childList) == 0xC);
|
||||
static_assert(offsetof(MEMHeapBase, spinlock) == 0x20);
|
||||
static_assert(offsetof(MEMHeapBase, flags) == 0x33);
|
||||
static_assert(sizeof(MEMHeapBase) == 0x34); // heap base is actually 0x40 but bytes 0x34 to 0x40 are padding?
|
||||
|
||||
typedef MEMHeapBase* MEMHeapHandle;
|
||||
|
||||
/* Heap base */
|
||||
|
||||
void MEMInitHeapBase(MEMHeapBase* heap, MEMHeapMagic magic, void* heapStart, void* heapEnd, uint32 createFlags);
|
||||
void MEMBaseDestroyHeap(MEMHeapBase* heap);
|
||||
|
||||
MEMHeapBase* MEMGetBaseHeapHandle(uint32 index);
|
||||
MEMHeapBase* MEMSetBaseHeapHandle(uint32 index, MEMHeapBase* heapBase);
|
||||
|
||||
/* Heap list */
|
||||
|
||||
bool MEMHeapTable_Add(MEMHeapBase* heap);
|
||||
bool MEMHeapTable_Remove(MEMHeapBase* heap);
|
||||
MEMHeapBase* _MEMList_FindContainingHeap(MEMList* list, MEMHeapBase* heap);
|
||||
bool MEMList_ContainsHeap(MEMList* list, MEMHeapBase* heap);
|
||||
MEMList* MEMList_FindContainingHeap(MEMHeapBase* head);
|
||||
|
||||
/* Heap settings */
|
||||
|
||||
enum class HEAP_FILL_TYPE : uint32
|
||||
{
|
||||
ON_HEAP_CREATE = 0,
|
||||
ON_ALLOC = 1,
|
||||
ON_FREE = 2,
|
||||
};
|
||||
|
||||
uint32 MEMGetFillValForHeap(HEAP_FILL_TYPE type);
|
||||
uint32 MEMSetFillValForHeap(HEAP_FILL_TYPE type, uint32 value);
|
||||
MEMHeapHandle MEMFindContainHeap(const void* memBlock);
|
||||
|
||||
/* Heap default allocators */
|
||||
|
||||
void InitDefaultHeaps(MEMPTR<MEMHeapBase>& mem1Heap, MEMPTR<MEMHeapBase>& memFGHeap, MEMPTR<MEMHeapBase>& mem2Heap);
|
||||
|
||||
void* default_MEMAllocFromDefaultHeap(uint32 size);
|
||||
void* default_MEMAllocFromDefaultHeapEx(uint32 size, sint32 alignment);
|
||||
void default_MEMFreeToDefaultHeap(void* mem);
|
||||
|
||||
void* _weak_MEMAllocFromDefaultHeapEx(uint32 size, sint32 alignment);
|
||||
void* _weak_MEMAllocFromDefaultHeap(uint32 size);
|
||||
void _weak_MEMFreeToDefaultHeap(void* ptr);
|
||||
|
||||
/* Unit heap */
|
||||
|
||||
void InitializeMEMUnitHeap();
|
||||
|
||||
void InitializeMEM();
|
||||
}
|
||||
596
src/Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.cpp
Normal file
596
src/Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.cpp
Normal file
|
|
@ -0,0 +1,596 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
MEMHeapHandle MEMInitBlockHeap(MEMBlockHeap2_t* memStart, void* startAddr, void* endAddr, void* initTrackMem, uint32 initTrackMemSize, uint32 createFlags)
|
||||
{
|
||||
if (memStart == nullptr || startAddr == nullptr || endAddr == nullptr || (uintptr_t)startAddr >= (uintptr_t)endAddr)
|
||||
return nullptr;
|
||||
|
||||
if (initTrackMemSize == 0)
|
||||
initTrackMem = nullptr;
|
||||
else if (initTrackMem == nullptr)
|
||||
initTrackMemSize = 0;
|
||||
|
||||
MEMInitHeapBase(memStart, MEMHeapMagic::BLOCK_HEAP, startAddr, endAddr, createFlags);
|
||||
|
||||
memStart->track.addrStart = startAddr;
|
||||
memStart->track.addrEnd = (void*)((uintptr_t)endAddr - 1);
|
||||
memStart->track.isFree = 1;
|
||||
memStart->track.previousBlock = nullptr;
|
||||
memStart->track.nextBlock = nullptr;
|
||||
|
||||
memStart->headBlock = &memStart->track;
|
||||
memStart->tailBlock = &memStart->track;
|
||||
memStart->nextFreeBlock = nullptr;
|
||||
memStart->freeBlocksLeft = 0;
|
||||
|
||||
uint8 flags = memStart->flags;
|
||||
if(HAS_FLAG(flags, MEM_HEAP_OPTION_FILL))
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
if(initTrackMemSize != 0)
|
||||
{
|
||||
sint32 result = MEMAddBlockHeapTracking(MEMPTR<void>(memStart).GetMPTR(), MEMPTR<void>(initTrackMem).GetMPTR(), initTrackMemSize);
|
||||
if(result != 0)
|
||||
{
|
||||
MEMBaseDestroyHeap(memStart);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MEMHeapTable_Add(memStart);
|
||||
return memStart;
|
||||
}
|
||||
|
||||
void* MEMDestroyBlockHeap(MEMHeapHandle hHeap)
|
||||
{
|
||||
if (!hHeap)
|
||||
return nullptr;
|
||||
cemu_assert_debug(hHeap->magic == MEMHeapMagic::BLOCK_HEAP);
|
||||
MEMBaseDestroyHeap(hHeap);
|
||||
MEMHeapTable_Remove(hHeap);
|
||||
memset(hHeap, 0x00, sizeof(MEMBlockHeap2_t));
|
||||
return hHeap;
|
||||
}
|
||||
|
||||
sint32 MEMGetAllocatableSizeForBlockHeapEx(MEMBlockHeap2_t* blockHeap, sint32 alignment)
|
||||
{
|
||||
if (!blockHeap || blockHeap->magic != MEMHeapMagic::BLOCK_HEAP)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "MEMGetAllocatableSizeForBlockHeapEx(): Not a valid block heap");
|
||||
return 0;
|
||||
}
|
||||
if (alignment < 0)
|
||||
alignment = -alignment;
|
||||
else if (alignment == 0)
|
||||
alignment = 4;
|
||||
|
||||
if (HAS_FLAG(blockHeap->flags, MEM_HEAP_OPTION_THREADSAFE))
|
||||
OSUninterruptibleSpinLock_Acquire(&blockHeap->spinlock);
|
||||
|
||||
MEMBlockHeapTrack2_t* track = (MEMBlockHeapTrack2_t*)blockHeap->headBlock.GetPtr();
|
||||
uint32 allocatableSize = 0;
|
||||
while (track)
|
||||
{
|
||||
if (track->isFree != 0)
|
||||
{
|
||||
uint32 addrStart = track->addrStart.GetMPTR();
|
||||
uint32 addrEnd = track->addrEnd.GetMPTR();
|
||||
uint32 alignmentMinusOne = alignment - 1;
|
||||
uint32 alignedAddrStart = ((addrStart + alignmentMinusOne) / alignment) * alignment;
|
||||
if (alignedAddrStart <= addrEnd)
|
||||
{
|
||||
uint32 size = (addrEnd + 1) - alignedAddrStart;
|
||||
allocatableSize = std::max(allocatableSize, size);
|
||||
}
|
||||
}
|
||||
// next
|
||||
track = (MEMBlockHeapTrack2_t*)track->nextBlock.GetPtr();
|
||||
}
|
||||
|
||||
if (HAS_FLAG(blockHeap->flags, MEM_HEAP_OPTION_THREADSAFE))
|
||||
OSUninterruptibleSpinLock_Release(&blockHeap->spinlock);
|
||||
|
||||
return allocatableSize;
|
||||
}
|
||||
|
||||
void MEMDumpBlockHeap(MPTR heap);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32 addrStart;
|
||||
/* +0x04 */ uint32 addrEnd;
|
||||
/* +0x08 */ uint32 isFree; // if 0 -> block is used
|
||||
/* +0x0C */ MPTR previousBlock;
|
||||
/* +0x10 */ MPTR nextBlock;
|
||||
}MEMBlockHeapTrackDEPR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32 ukn00;
|
||||
/* +0x04 */ uint32 ukn04;
|
||||
/* +0x08 */ uint32 trackArray;
|
||||
/* +0x0C */ uint32 trackCount;
|
||||
}MEMBlockHeapGroupDEPR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x000 */ betype<MEMHeapMagic> magic;
|
||||
/* +0x004 */ MEMLink_t link;
|
||||
/* +0x00C */ MEMList_t childList;
|
||||
/* +0x018 */ MPTR heapStart;
|
||||
/* +0x01C */ MPTR heapEnd;
|
||||
/* +0x020 */ coreinit::OSSpinLock spinlock;
|
||||
/* +0x030 */
|
||||
union
|
||||
{
|
||||
uint32 val;
|
||||
uint32 flags;
|
||||
}
|
||||
attribute;
|
||||
// start of block heap header
|
||||
/* +0x034 */ uint8 ukn034[0x50 - 0x34]; // ?
|
||||
/* +0x050 */ MEMBlockHeapTrackDEPR nullBlockTrack;
|
||||
/* +0x064 */ MPTR headBlock;
|
||||
/* +0x068 */ MPTR tailBlock;
|
||||
/* +0x06C */ MPTR nextFreeBlock;
|
||||
/* +0x070 */ uint32 freeBlocksLeft;
|
||||
}MEMBlockHeapDEPR;
|
||||
|
||||
void _blockHeapDebugVerifyIfBlockIsLinked(MEMBlockHeapDEPR* blockHeapHead, MEMBlockHeapTrackDEPR* track)
|
||||
{
|
||||
//MEMBlockHeapTrack_t* trackItr = (MEMBlockHeapTrack_t*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->firstBlock));
|
||||
//MEMBlockHeapTrack_t* prevHistory[4];
|
||||
//while( trackItr )
|
||||
//{
|
||||
// if( trackItr == track )
|
||||
// {
|
||||
// debug_printf("PrevBlock3 %08x\n", memory_getVirtualOffsetFromPointer(prevHistory[0]));
|
||||
// debug_printf("PrevBlock2 %08x\n", memory_getVirtualOffsetFromPointer(prevHistory[1]));
|
||||
// debug_printf("PrevBlock1 %08x\n", memory_getVirtualOffsetFromPointer(prevHistory[2]));
|
||||
// debug_printf("PrevBlock0 %08x\n", memory_getVirtualOffsetFromPointer(prevHistory[3]));
|
||||
// assert_dbg();
|
||||
// }
|
||||
// prevHistory[3] = prevHistory[2];
|
||||
// prevHistory[2] = prevHistory[1];
|
||||
// prevHistory[1] = prevHistory[0];
|
||||
// prevHistory[0] = trackItr;
|
||||
// // next
|
||||
// trackItr = (MEMBlockHeapTrack_t*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(trackItr->nextBlock));
|
||||
//}
|
||||
}
|
||||
|
||||
void _blockHeapDebugVerifyLinkOrder(MEMBlockHeapDEPR* blockHeapHead)
|
||||
{
|
||||
//MEMBlockHeapTrack_t* trackItr = (MEMBlockHeapTrack_t*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->firstBlock));
|
||||
//MEMBlockHeapTrack_t* prev = NULL;
|
||||
//while( trackItr )
|
||||
//{
|
||||
// MPTR prevMPTR = memory_getVirtualOffsetFromPointer(prev);
|
||||
// if( _swapEndianU32(trackItr->previousBlock) != prevMPTR )
|
||||
// assert_dbg();
|
||||
// // next
|
||||
// prev = trackItr;
|
||||
// trackItr = (MEMBlockHeapTrack_t*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(trackItr->nextBlock));
|
||||
//}
|
||||
}
|
||||
|
||||
sint32 MEMAddBlockHeapTracking(MPTR heap, MPTR trackMem, uint32 trackMemSize)
|
||||
{
|
||||
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
|
||||
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (trackMem == MPTR_NULL || trackMemSize <= (sizeof(MEMBlockHeapGroupDEPR) + sizeof(MEMBlockHeapTrackDEPR)))
|
||||
{
|
||||
return -4;
|
||||
}
|
||||
uint32 trackCount = (trackMemSize - sizeof(MEMBlockHeapGroupDEPR)) / sizeof(MEMBlockHeapTrackDEPR);
|
||||
MEMBlockHeapGroupDEPR* group = (MEMBlockHeapGroupDEPR*)memory_getPointerFromVirtualOffset(trackMem);
|
||||
group->ukn00 = _swapEndianU32(0);
|
||||
group->ukn04 = _swapEndianU32(0);
|
||||
group->trackArray = _swapEndianU32(trackMem + sizeof(MEMBlockHeapGroupDEPR));
|
||||
group->trackCount = _swapEndianU32(trackCount);
|
||||
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)(group + 1);
|
||||
track[0].previousBlock = _swapEndianU32(0);
|
||||
if (trackCount > 1)
|
||||
{
|
||||
for (uint32 i = 0; i < trackCount - 1; i++)
|
||||
{
|
||||
track[i].nextBlock = _swapEndianU32(memory_getVirtualOffsetFromPointer(track + i + 1));
|
||||
track[i + 1].previousBlock = _swapEndianU32(0);
|
||||
}
|
||||
}
|
||||
|
||||
// todo: Use spinlock from heap (and only use it if threadsafe heap flag is set)
|
||||
__OSLockScheduler();
|
||||
track[trackCount - 1].nextBlock = blockHeapHead->nextFreeBlock;
|
||||
blockHeapHead->nextFreeBlock = _swapEndianU32(memory_getVirtualOffsetFromPointer(track));
|
||||
blockHeapHead->freeBlocksLeft = _swapEndianU32(_swapEndianU32(blockHeapHead->freeBlocksLeft) + trackCount);
|
||||
__OSUnlockScheduler();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 MEMGetTrackingLeftInBlockHeap(MPTR heap)
|
||||
{
|
||||
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
|
||||
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return _swapEndianU32(blockHeapHead->freeBlocksLeft);
|
||||
}
|
||||
|
||||
MPTR _MEMBlockHeap_GetFreeBlockTrack(MEMBlockHeapDEPR* blockHeapHead)
|
||||
{
|
||||
MPTR trackMPTR = _swapEndianU32(blockHeapHead->nextFreeBlock);
|
||||
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(trackMPTR);
|
||||
MPTR nextFreeBlockMPTR = _swapEndianU32(track->nextBlock); // for unreserved tracks, nextBlock holds the next unreserved block
|
||||
track->nextBlock = _swapEndianU32(MPTR_NULL);
|
||||
blockHeapHead->nextFreeBlock = _swapEndianU32(nextFreeBlockMPTR);
|
||||
if (_swapEndianU32(blockHeapHead->freeBlocksLeft) == 0)
|
||||
{
|
||||
forceLog_printf("BlockHeap: No free blocks left\n");
|
||||
assert_dbg();
|
||||
}
|
||||
blockHeapHead->freeBlocksLeft = _swapEndianU32(_swapEndianU32(blockHeapHead->freeBlocksLeft) - 1);
|
||||
return trackMPTR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release MEMBlockHeapTrack_t struct for block heap
|
||||
*/
|
||||
void _MEMBlockHeap_ReleaseBlockTrack(MEMBlockHeapDEPR* blockHeapHead, MPTR trackMPTR)
|
||||
{
|
||||
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(trackMPTR);
|
||||
track->nextBlock = blockHeapHead->nextFreeBlock;
|
||||
blockHeapHead->nextFreeBlock = _swapEndianU32(trackMPTR);
|
||||
blockHeapHead->freeBlocksLeft = _swapEndianU32(_swapEndianU32(blockHeapHead->freeBlocksLeft) + 1);
|
||||
}
|
||||
|
||||
sint32 _MEMBlockHeap_AllocAtBlock(MEMBlockHeapDEPR* blockHeapHead, MEMBlockHeapTrackDEPR* track, MPTR allocationAddress, uint32 size)
|
||||
{
|
||||
MPTR trackMPTR = memory_getVirtualOffsetFromPointer(track);
|
||||
uint32 trackEndAddr = _swapEndianU32(track->addrEnd);
|
||||
uint32 prefixBlockSize = allocationAddress - _swapEndianU32(track->addrStart);
|
||||
uint32 suffixBlockSize = (_swapEndianU32(track->addrEnd) + 1) - (allocationAddress + size);
|
||||
if (prefixBlockSize > 0 && suffixBlockSize > 0)
|
||||
{
|
||||
// requires two free blocks
|
||||
if (_swapEndianU32(blockHeapHead->freeBlocksLeft) < 2)
|
||||
return -1;
|
||||
}
|
||||
else if (prefixBlockSize > 0 || suffixBlockSize > 0)
|
||||
{
|
||||
// requires one free block
|
||||
if (_swapEndianU32(blockHeapHead->freeBlocksLeft) < 1)
|
||||
return -2;
|
||||
}
|
||||
MPTR currentPreviousTrack = _swapEndianU32(track->previousBlock);
|
||||
// remove used block from chain of free blocks (debug)
|
||||
if (track->isFree != _swapEndianU32(0))
|
||||
{
|
||||
// check if block is in list of free blocks (it shouldnt be)
|
||||
MEMBlockHeapTrackDEPR* trackItr = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->nextFreeBlock));
|
||||
while (trackItr)
|
||||
{
|
||||
if (trackItr == track)
|
||||
assert_dbg();
|
||||
// next
|
||||
trackItr = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(trackItr->nextBlock));
|
||||
}
|
||||
}
|
||||
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
|
||||
// create prefix block
|
||||
if (prefixBlockSize > 0)
|
||||
{
|
||||
uint32 blockRangeStart = _swapEndianU32(track->addrStart);
|
||||
uint32 blockRangeEnd = allocationAddress - 1;
|
||||
MPTR prefixTrackMPTR = _MEMBlockHeap_GetFreeBlockTrack(blockHeapHead);
|
||||
MEMBlockHeapTrackDEPR* prefixTrack = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(prefixTrackMPTR);
|
||||
prefixTrack->isFree = _swapEndianU32(1);
|
||||
prefixTrack->addrStart = _swapEndianU32(blockRangeStart);
|
||||
prefixTrack->addrEnd = _swapEndianU32(blockRangeEnd);
|
||||
// register new firstBlock
|
||||
if (blockHeapHead->headBlock == _swapEndianU32(trackMPTR))
|
||||
blockHeapHead->headBlock = _swapEndianU32(prefixTrackMPTR);
|
||||
// update link in original previous block
|
||||
if (_swapEndianU32(track->previousBlock) != MPTR_NULL)
|
||||
{
|
||||
MEMBlockHeapTrackDEPR* tempTrack = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(_swapEndianU32(track->previousBlock));
|
||||
tempTrack->nextBlock = _swapEndianU32(prefixTrackMPTR);
|
||||
}
|
||||
// update previous/next track link
|
||||
prefixTrack->nextBlock = _swapEndianU32(trackMPTR);
|
||||
prefixTrack->previousBlock = _swapEndianU32(currentPreviousTrack);
|
||||
// set prefix track as current previous track
|
||||
currentPreviousTrack = prefixTrackMPTR;
|
||||
}
|
||||
// update used block
|
||||
track->isFree = _swapEndianU32(0);
|
||||
track->addrStart = _swapEndianU32(allocationAddress);
|
||||
track->addrEnd = _swapEndianU32(allocationAddress + size - 1);
|
||||
track->previousBlock = _swapEndianU32(currentPreviousTrack);
|
||||
currentPreviousTrack = trackMPTR;
|
||||
// create suffix block
|
||||
if (suffixBlockSize > 0)
|
||||
{
|
||||
uint32 blockRangeStart = allocationAddress + size;
|
||||
uint32 blockRangeEnd = trackEndAddr;
|
||||
// get suffix track
|
||||
MPTR suffixTrackMPTR = _MEMBlockHeap_GetFreeBlockTrack(blockHeapHead);
|
||||
MEMBlockHeapTrackDEPR* suffixTrack = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(suffixTrackMPTR);
|
||||
suffixTrack->isFree = _swapEndianU32(1);
|
||||
suffixTrack->addrStart = _swapEndianU32(blockRangeStart);
|
||||
suffixTrack->addrEnd = _swapEndianU32(blockRangeEnd);
|
||||
// update previous/next track link
|
||||
suffixTrack->previousBlock = _swapEndianU32(currentPreviousTrack);
|
||||
suffixTrack->nextBlock = track->nextBlock;
|
||||
// update last block of heap
|
||||
if (_swapEndianU32(blockHeapHead->tailBlock) == trackMPTR)
|
||||
blockHeapHead->tailBlock = _swapEndianU32(suffixTrackMPTR);
|
||||
// update link in original next block
|
||||
if (_swapEndianU32(track->nextBlock) != MPTR_NULL)
|
||||
{
|
||||
MEMBlockHeapTrackDEPR* tempTrack = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(_swapEndianU32(track->nextBlock));
|
||||
tempTrack->previousBlock = _swapEndianU32(suffixTrackMPTR);
|
||||
}
|
||||
// update next block
|
||||
track->nextBlock = _swapEndianU32(suffixTrackMPTR);
|
||||
}
|
||||
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
|
||||
// todo: Get fill value via MEMGetFillValForHeap
|
||||
memset(memory_getPointerFromVirtualOffset(allocationAddress), 0x00, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MEMBlockHeapTrackDEPR* _MEMBlockHeap_FindBlockContaining(MEMBlockHeapDEPR* blockHeapHead, MPTR memAddr)
|
||||
{
|
||||
MPTR heapStart = _swapEndianU32(blockHeapHead->heapStart);
|
||||
MPTR heapEnd = _swapEndianU32(blockHeapHead->heapEnd);
|
||||
if (memAddr < heapStart)
|
||||
return NULL;
|
||||
if (memAddr > heapEnd)
|
||||
return NULL;
|
||||
uint32 distanceToStart = memAddr - heapStart;
|
||||
uint32 distanceToEnd = heapEnd - memAddr;
|
||||
// todo: If distanceToStart < distanceToEnd -> Iterate starting from firstBlock, else iterate starting from lastBlock (and continue via track->previousBlock)
|
||||
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->headBlock));
|
||||
if (track == NULL)
|
||||
return NULL;
|
||||
while (track)
|
||||
{
|
||||
if (_swapEndianU32(track->addrStart) == memAddr)
|
||||
return track;
|
||||
// next
|
||||
// todo: Should this be ->previousBlock ?
|
||||
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(track->nextBlock));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MPTR MEMAllocFromBlockHeapEx(MPTR heap, uint32 size, sint32 alignment)
|
||||
{
|
||||
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
|
||||
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
|
||||
{
|
||||
return MPTR_NULL;
|
||||
}
|
||||
// find free block which can hold the data (including alignment)
|
||||
__OSLockScheduler(); // todo: replace with spinlock from heap
|
||||
if (alignment == 0)
|
||||
alignment = 4;
|
||||
bool allocateAtEndOfBlock = false;
|
||||
if (alignment < 0)
|
||||
{
|
||||
allocateAtEndOfBlock = true;
|
||||
alignment = -alignment;
|
||||
}
|
||||
MEMBlockHeapTrackDEPR* track;
|
||||
if (allocateAtEndOfBlock)
|
||||
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->tailBlock));
|
||||
else
|
||||
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->headBlock));
|
||||
|
||||
cemu_assert_debug(__popcnt(alignment) == 1); // not a supported alignment value
|
||||
while (track)
|
||||
{
|
||||
if (track->isFree != _swapEndianU32(0))
|
||||
{
|
||||
uint32 blockRangeStart = _swapEndianU32(track->addrStart);
|
||||
uint32 blockRangeEnd = _swapEndianU32(track->addrEnd) + 1;
|
||||
if (allocateAtEndOfBlock == false)
|
||||
{
|
||||
// calculate remaining size with proper alignment
|
||||
uint32 alignedBlockRangeStart = (blockRangeStart + alignment - 1) & ~(alignment - 1);
|
||||
if (alignedBlockRangeStart < blockRangeEnd)
|
||||
{
|
||||
uint32 allocRange = blockRangeEnd - alignedBlockRangeStart;
|
||||
if (allocRange >= size)
|
||||
{
|
||||
sint32 allocError = _MEMBlockHeap_AllocAtBlock(blockHeapHead, track, alignedBlockRangeStart, size);
|
||||
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
|
||||
__OSUnlockScheduler();
|
||||
if (allocError == 0)
|
||||
return alignedBlockRangeStart;
|
||||
return MPTR_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 alignedBlockRangeStart = (blockRangeEnd + 1 - size) & ~(alignment - 1);
|
||||
if (alignedBlockRangeStart >= blockRangeStart)
|
||||
{
|
||||
sint32 allocError = _MEMBlockHeap_AllocAtBlock(blockHeapHead, track, alignedBlockRangeStart, size);
|
||||
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
|
||||
__OSUnlockScheduler();
|
||||
if (allocError == 0)
|
||||
return alignedBlockRangeStart;
|
||||
return MPTR_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
// next
|
||||
if (allocateAtEndOfBlock)
|
||||
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(track->previousBlock));
|
||||
else
|
||||
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(track->nextBlock));
|
||||
if (track == nullptr)
|
||||
break;
|
||||
}
|
||||
__OSUnlockScheduler();
|
||||
return MPTR_NULL;
|
||||
}
|
||||
|
||||
void MEMFreeToBlockHeap(MPTR heap, MPTR memAddr)
|
||||
{
|
||||
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
|
||||
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
|
||||
{
|
||||
return;
|
||||
}
|
||||
__OSLockScheduler(); // todo: replace with spinlock from heap (if heap threadsafe flag is set)
|
||||
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
|
||||
MEMBlockHeapTrackDEPR* block = _MEMBlockHeap_FindBlockContaining(blockHeapHead, memAddr);
|
||||
if (block != NULL)
|
||||
{
|
||||
MPTR blockMPTR = memory_getVirtualOffsetFromPointer(block);
|
||||
#ifndef PUBLIC_RELEASE
|
||||
if (block->isFree != 0)
|
||||
assert_dbg();
|
||||
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
|
||||
#endif
|
||||
// mark current block as free
|
||||
block->isFree = _swapEndianU32(1);
|
||||
// attempt to merge with previous block
|
||||
if (_swapEndianU32(block->previousBlock) != NULL)
|
||||
{
|
||||
MPTR previousBlockMPTR = _swapEndianU32(block->previousBlock);
|
||||
MEMBlockHeapTrackDEPR* previousBlock = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(previousBlockMPTR);
|
||||
if (_swapEndianU32(previousBlock->isFree) != 0)
|
||||
{
|
||||
// merge
|
||||
previousBlock->addrEnd = block->addrEnd;
|
||||
previousBlock->nextBlock = block->nextBlock;
|
||||
if (_swapEndianU32(block->nextBlock) != MPTR_NULL)
|
||||
{
|
||||
MEMBlockHeapTrackDEPR* tempNextBlock = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(_swapEndianU32(block->nextBlock));
|
||||
tempNextBlock->previousBlock = _swapEndianU32(previousBlockMPTR);
|
||||
}
|
||||
// release removed block
|
||||
_MEMBlockHeap_ReleaseBlockTrack(blockHeapHead, blockMPTR);
|
||||
// debug
|
||||
_blockHeapDebugVerifyIfBlockIsLinked(blockHeapHead, (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(blockMPTR));
|
||||
// merged block becomes the new current block
|
||||
blockMPTR = previousBlockMPTR;
|
||||
block = previousBlock;
|
||||
}
|
||||
}
|
||||
// attempt to merge with next block
|
||||
if (_swapEndianU32(block->nextBlock) != NULL)
|
||||
{
|
||||
MPTR nextBlockMPTR = _swapEndianU32(block->nextBlock);
|
||||
MEMBlockHeapTrackDEPR* nextBlock = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(nextBlockMPTR);
|
||||
if (_swapEndianU32(nextBlock->isFree) != 0)
|
||||
{
|
||||
// merge
|
||||
block->addrEnd = nextBlock->addrEnd;
|
||||
block->nextBlock = nextBlock->nextBlock;
|
||||
if (_swapEndianU32(nextBlock->nextBlock) != MPTR_NULL)
|
||||
{
|
||||
MEMBlockHeapTrackDEPR* tempNextBlock = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(_swapEndianU32(nextBlock->nextBlock));
|
||||
tempNextBlock->previousBlock = _swapEndianU32(blockMPTR);
|
||||
}
|
||||
//// merged block becomes the new current block
|
||||
//blockMPTR = previousBlockMPTR;
|
||||
//block = previousBlock;
|
||||
// update last block
|
||||
if (_swapEndianU32(blockHeapHead->tailBlock) == nextBlockMPTR)
|
||||
{
|
||||
blockHeapHead->tailBlock = _swapEndianU32(blockMPTR);
|
||||
}
|
||||
// release removed block
|
||||
_MEMBlockHeap_ReleaseBlockTrack(blockHeapHead, nextBlockMPTR);
|
||||
// debug
|
||||
_blockHeapDebugVerifyIfBlockIsLinked(blockHeapHead, (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(nextBlockMPTR));
|
||||
}
|
||||
}
|
||||
}
|
||||
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
uint32 MEMGetTotalFreeSizeForBlockHeap(MEMBlockHeapDEPR* blockHeap)
|
||||
{
|
||||
if (!blockHeap || blockHeap->magic != MEMHeapMagic::BLOCK_HEAP)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
return 0;
|
||||
}
|
||||
|
||||
__OSLockScheduler(); // todo: replace with spinlock from heap (if heap threadsafe flag is set)
|
||||
uint32 totalSize = 0;
|
||||
// sum up all free blocks
|
||||
MPTR blockMPTR = _swapEndianU32(blockHeap->headBlock);
|
||||
while (blockMPTR)
|
||||
{
|
||||
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(blockMPTR);
|
||||
if (track->isFree != _swapEndianU32(0))
|
||||
{
|
||||
// get block size
|
||||
uint32 blockSize = _swapEndianU32(track->addrEnd) - _swapEndianU32(track->addrStart) + 1;
|
||||
// add to totalSize
|
||||
totalSize += blockSize;
|
||||
}
|
||||
// next
|
||||
blockMPTR = _swapEndianU32(track->nextBlock);
|
||||
}
|
||||
__OSUnlockScheduler(); // todo: replace with spinlock from heap (if heap threadsafe flag is set)
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
void MEMDumpBlockHeap(MPTR heap)
|
||||
{
|
||||
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
|
||||
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
return;
|
||||
}
|
||||
// iterate reserved/sized blocks
|
||||
debug_printf("### MEMDumpBlockHeap ###\n");
|
||||
__OSLockScheduler(); // todo: replace with spinlock from heap
|
||||
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->headBlock));
|
||||
while (track)
|
||||
{
|
||||
uint32 blockRangeStart = _swapEndianU32(track->addrStart);
|
||||
uint32 blockRangeEnd = _swapEndianU32(track->addrEnd) + 1;
|
||||
debug_printf(" %08x %08x - %08x prev/next %08x %08x isFree: %d\n", memory_getVirtualOffsetFromPointer(track), blockRangeStart, blockRangeEnd, _swapEndianU32(track->previousBlock), _swapEndianU32(track->nextBlock), _swapEndianU32(track->isFree));
|
||||
// next
|
||||
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(track->nextBlock));
|
||||
}
|
||||
debug_printf("\n");
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void InitializeMEMBlockHeap()
|
||||
{
|
||||
cafeExportRegister("coreinit", MEMInitBlockHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMDestroyBlockHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMGetAllocatableSizeForBlockHeapEx, LogType::CoreinitMem);
|
||||
|
||||
cafeExportRegister("coreinit", MEMAddBlockHeapTracking, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMGetTrackingLeftInBlockHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMAllocFromBlockHeapEx, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMFreeToBlockHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMGetTotalFreeSizeForBlockHeap, LogType::CoreinitMem);
|
||||
}
|
||||
}
|
||||
34
src/Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.h
Normal file
34
src/Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct MEMBlockHeapTrack2_t
|
||||
{
|
||||
/* +0x00 */ MEMPTR<void> addrStart;
|
||||
/* +0x04 */ MEMPTR<void> addrEnd;
|
||||
/* +0x08 */ uint32be isFree; // if 0 -> block is used
|
||||
/* +0x0C */ MEMPTR<void> previousBlock;
|
||||
/* +0x10 */ MEMPTR<void> nextBlock;
|
||||
};
|
||||
static_assert(sizeof(MEMBlockHeapTrack2_t) == 0x14);
|
||||
|
||||
struct MEMBlockHeap2_t : MEMHeapBase
|
||||
{
|
||||
/* +0x34 */ uint8 ukn[0x50 - 0x34];
|
||||
/* +0x50 */ MEMBlockHeapTrack2_t track;
|
||||
/* +0x64 */ MEMPTR<void> headBlock;
|
||||
/* +0x68 */ MEMPTR<void> tailBlock;
|
||||
/* +0x6C */ MEMPTR<void> nextFreeBlock;
|
||||
/* +0x70 */ uint32be freeBlocksLeft;
|
||||
/* +0x74 */ uint8 padding[0x80 - 0x74];
|
||||
};
|
||||
static_assert(sizeof(MEMBlockHeap2_t) == 0x80);
|
||||
|
||||
MEMHeapHandle MEMInitBlockHeap(MEMBlockHeap2_t* memStart, void* startAddr, void* endAddr, void* initTrackMem, uint32 initTrackMemSize, uint32 createFlags);
|
||||
void* MEMDestroyBlockHeap(MEMHeapHandle hHeap);
|
||||
|
||||
sint32 MEMAddBlockHeapTracking(MPTR heap, MPTR trackMem, uint32 trackMemSize);
|
||||
|
||||
void InitializeMEMBlockHeap();
|
||||
}
|
||||
1105
src/Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.cpp
Normal file
1105
src/Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.cpp
Normal file
File diff suppressed because it is too large
Load diff
52
src/Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h
Normal file
52
src/Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void expheap_load();
|
||||
|
||||
#define MEM_EXPHEAP_ALLOC_MODE_FIRST (0)
|
||||
#define MEM_EXPHEAP_ALLOC_MODE_NEAR (1)
|
||||
#define MEM_EXPHEAP_USE_ALIGN_MARGIN (2)
|
||||
|
||||
enum class MEMExpHeapAllocDirection : uint32
|
||||
{
|
||||
HEAD = 0,
|
||||
TAIL = 1
|
||||
};
|
||||
|
||||
struct MBlockChain2_t
|
||||
{
|
||||
MEMPTR<struct MBlock2_t> headMBlock; // 0x00
|
||||
MEMPTR<struct MBlock2_t> tailMBlock; // 0x04
|
||||
};
|
||||
static_assert(sizeof(MBlockChain2_t) == 8);
|
||||
|
||||
struct MEMExpHeapHead40_t
|
||||
{
|
||||
/* +0x00 */ MBlockChain2_t chainFreeBlocks; // 0x00
|
||||
/* +0x08 */ MBlockChain2_t chainUsedBlocks; // 0x08
|
||||
/* +0x10 */ uint16 groupID;
|
||||
/* +0x12 */ uint16 fields; // Bit 0 -> Alloc mode, Bit 1 -> Allocate within alignment (create free blocks inside alignment padding)
|
||||
};
|
||||
|
||||
static_assert(sizeof(MEMExpHeapHead40_t) == 0x14);
|
||||
|
||||
struct MEMExpHeapHead2 : MEMHeapBase
|
||||
{
|
||||
// Base
|
||||
/* +0x34 */ uint32be ukn34;
|
||||
/* +0x38 */ uint32be ukn38;
|
||||
/* +0x3C */ uint32be ukn3C;
|
||||
/* +0x40 */ MEMExpHeapHead40_t expHeapHead;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MEMExpHeapHead2) == 0x54);
|
||||
|
||||
MEMHeapHandle MEMCreateExpHeapEx(void* startAddress, uint32 size, uint32 createFlags);
|
||||
void* MEMAllocFromExpHeapEx(MEMHeapHandle heap, uint32 size, sint32 alignment);
|
||||
void MEMFreeToExpHeap(MEMHeapHandle heap, void* mem);
|
||||
uint32 MEMGetAllocatableSizeForExpHeapEx(MEMHeapHandle heap, sint32 alignment);
|
||||
}
|
||||
246
src/Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.cpp
Normal file
246
src/Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.cpp
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
bool __FrmHeapDebug_IsValid(MEMFrmHeap* frmHeap, const char* funcName)
|
||||
{
|
||||
if (!frmHeap)
|
||||
{
|
||||
cemuLog_log(LogType::APIErrors, "{}: Heap is nullptr", funcName);
|
||||
return false;
|
||||
}
|
||||
if (frmHeap->magic != MEMHeapMagic::FRAME_HEAP)
|
||||
{
|
||||
cemuLog_log(LogType::APIErrors, "{}: Heap has bad magic. Not initialized?", funcName);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MEMFrmHeap* MEMCreateFrmHeapEx(void* memStart, uint32 size, uint32 createFlags)
|
||||
{
|
||||
cemu_assert_debug(memStart);
|
||||
|
||||
uintptr_t startAddr = (uintptr_t)memStart;
|
||||
uintptr_t endAddr = startAddr + size;
|
||||
|
||||
// align and pad address
|
||||
startAddr = (startAddr + 3) & ~3;
|
||||
endAddr &= ~3;
|
||||
|
||||
if (startAddr == 0)
|
||||
return nullptr;
|
||||
if (startAddr > endAddr || (endAddr - startAddr) < sizeof(MEMFrmHeap))
|
||||
return nullptr;
|
||||
|
||||
MEMFrmHeap* frmHeap = (MEMFrmHeap*)startAddr;
|
||||
MEMInitHeapBase(frmHeap, MEMHeapMagic::FRAME_HEAP, (void*)((uintptr_t)startAddr + sizeof(MEMFrmHeap)), (void*)endAddr, createFlags);
|
||||
frmHeap->allocationHead = frmHeap->heapStart;
|
||||
frmHeap->allocationTail = frmHeap->heapEnd;
|
||||
frmHeap->recordedStates = nullptr;
|
||||
|
||||
MEMHeapTable_Add(frmHeap);
|
||||
return frmHeap;
|
||||
}
|
||||
|
||||
void* MEMDestroyFrmHeap(MEMFrmHeap* frmHeap)
|
||||
{
|
||||
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMDestroyFrmHeap"))
|
||||
return nullptr;
|
||||
MEMBaseDestroyHeap(frmHeap);
|
||||
MEMHeapTable_Remove(frmHeap);
|
||||
return frmHeap;
|
||||
}
|
||||
|
||||
uint32 MEMGetAllocatableSizeForFrmHeapEx(MEMFrmHeap* frmHeap, sint32 alignment)
|
||||
{
|
||||
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMGetAllocatableSizeForFrmHeapEx"))
|
||||
return 0;
|
||||
frmHeap->AcquireLock();
|
||||
uint32 allocatableSize = 0;
|
||||
bool negativeAlignment = alignment < 0;
|
||||
if (negativeAlignment)
|
||||
alignment = -alignment;
|
||||
if (!std::has_single_bit<uint32>((uint32)alignment))
|
||||
{
|
||||
cemuLog_log(LogType::APIErrors, "MEMGetAllocatableSizeForFrmHeapEx(): Invalid alignment");
|
||||
return 0;
|
||||
}
|
||||
if (negativeAlignment)
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 headAllocator = frmHeap->allocationHead.GetMPTR();
|
||||
uint32 tailAllocator = frmHeap->allocationTail.GetMPTR();
|
||||
uint32 allocStart = (headAllocator + alignment - 1) & ~(alignment - 1);
|
||||
if (allocStart <= tailAllocator)
|
||||
{
|
||||
allocatableSize = tailAllocator - allocStart;
|
||||
}
|
||||
}
|
||||
frmHeap->ReleaseLock();
|
||||
return allocatableSize;
|
||||
}
|
||||
|
||||
void* MEMiGetFreeStartForFrmHeap(MEMFrmHeap* heap)
|
||||
{
|
||||
if (!__FrmHeapDebug_IsValid(heap, "MEMiGetFreeStartForFrmHeap"))
|
||||
return nullptr;
|
||||
return heap->allocationHead;
|
||||
}
|
||||
|
||||
void* MEMiGetFreeEndForFrmHeap(MEMFrmHeap* heap)
|
||||
{
|
||||
if (!__FrmHeapDebug_IsValid(heap, "MEMiGetFreeEndForFrmHeap"))
|
||||
return nullptr;
|
||||
return heap->allocationTail;
|
||||
}
|
||||
|
||||
void* __FrmHeap_AllocFromHead(MEMFrmHeap* frmHeap, uint32 size, sint32 alignment)
|
||||
{
|
||||
uint32 head = frmHeap->allocationHead.GetMPTR();
|
||||
uint32 tail = frmHeap->allocationTail.GetMPTR();
|
||||
uint32 allocStart = (head + alignment - 1) & ~(alignment - 1);
|
||||
if ((allocStart + size) <= tail)
|
||||
{
|
||||
auto prevHead = frmHeap->allocationHead;
|
||||
frmHeap->allocationHead = allocStart + size;
|
||||
if (frmHeap->HasOptionClear())
|
||||
memset(prevHead.GetPtr(), 0, frmHeap->allocationHead.GetMPTR() - prevHead.GetMPTR());
|
||||
return MEMPTR<void>(allocStart).GetPtr();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* __FrmHeap_AllocFromTail(MEMFrmHeap* frmHeap, uint32 size, sint32 alignment)
|
||||
{
|
||||
uint32 head = frmHeap->allocationHead.GetMPTR();
|
||||
uint32 tail = frmHeap->allocationTail.GetMPTR();
|
||||
uint32 allocStart = (tail - size) & ~(alignment - 1);
|
||||
if (allocStart >= head)
|
||||
{
|
||||
auto prevTail = frmHeap->allocationTail;
|
||||
frmHeap->allocationTail = allocStart;
|
||||
if (frmHeap->HasOptionClear())
|
||||
memset(frmHeap->allocationTail.GetPtr(), 0, prevTail.GetMPTR() - frmHeap->allocationTail.GetMPTR());
|
||||
return MEMPTR<void>(allocStart).GetPtr();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* MEMAllocFromFrmHeapEx(MEMFrmHeap* frmHeap, uint32 size, sint32 alignment)
|
||||
{
|
||||
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMAllocFromFrmHeapEx"))
|
||||
return nullptr;
|
||||
if (size == 0)
|
||||
size = 4;
|
||||
size = (size + 3) & ~3; // pad to 4 byte alignment
|
||||
frmHeap->AcquireLock();
|
||||
void* mem;
|
||||
if (alignment >= 0)
|
||||
mem = __FrmHeap_AllocFromHead(frmHeap, size, alignment);
|
||||
else
|
||||
mem = __FrmHeap_AllocFromTail(frmHeap, size, -alignment);
|
||||
frmHeap->ReleaseLock();
|
||||
return mem;
|
||||
}
|
||||
|
||||
void __FrmHeap_FreeFromHead(MEMFrmHeap* frmHeap)
|
||||
{
|
||||
cemu_assert_debug(frmHeap->recordedStates.IsNull());
|
||||
frmHeap->recordedStates = nullptr;
|
||||
frmHeap->allocationHead = frmHeap->heapStart;
|
||||
}
|
||||
|
||||
void __FrmHeap_FreeFromTail(MEMFrmHeap* frmHeap)
|
||||
{
|
||||
cemu_assert_debug(frmHeap->recordedStates.IsNull());
|
||||
frmHeap->recordedStates = nullptr;
|
||||
void* heapEnd = frmHeap->heapEnd.GetPtr();
|
||||
frmHeap->allocationTail = heapEnd;
|
||||
}
|
||||
|
||||
void MEMFreeToFrmHeap(MEMFrmHeap* frmHeap, FrmHeapMode mode)
|
||||
{
|
||||
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMFreeToFrmHeap"))
|
||||
return;
|
||||
frmHeap->AcquireLock();
|
||||
|
||||
if ((mode & FrmHeapMode::Head) != 0)
|
||||
__FrmHeap_FreeFromHead(frmHeap);
|
||||
|
||||
if ((mode & FrmHeapMode::Tail) != 0)
|
||||
__FrmHeap_FreeFromTail(frmHeap);
|
||||
|
||||
frmHeap->ReleaseLock();
|
||||
}
|
||||
|
||||
bool MEMRecordStateForFrmHeap(MEMFrmHeap* frmHeap, uint32 id)
|
||||
{
|
||||
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMRecordStateForFrmHeap"))
|
||||
return false;
|
||||
frmHeap->AcquireLock();
|
||||
auto allocationHead = frmHeap->allocationHead;
|
||||
auto allocationTail = frmHeap->allocationTail;
|
||||
MEMFrmHeapRecordedState* rState = (MEMFrmHeapRecordedState*)__FrmHeap_AllocFromHead(frmHeap, sizeof(MEMFrmHeapRecordedState), 4); // modifies memHeap->allocationHead
|
||||
cemu_assert_debug(rState);
|
||||
if (!rState)
|
||||
{
|
||||
frmHeap->ReleaseLock();
|
||||
return false;
|
||||
}
|
||||
rState->id = id;
|
||||
rState->allocationHead = allocationHead;
|
||||
rState->allocationTail = allocationTail;
|
||||
rState->prevRecordedState = frmHeap->recordedStates;
|
||||
frmHeap->recordedStates = rState;
|
||||
frmHeap->ReleaseLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MEMFreeByStateToFrmHeap(MEMFrmHeap* frmHeap, uint32 id)
|
||||
{
|
||||
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMFreeByStateToFrmHeap"))
|
||||
return false;
|
||||
frmHeap->AcquireLock();
|
||||
// find matching state
|
||||
MEMFrmHeapRecordedState* rState = frmHeap->recordedStates.GetPtr();
|
||||
while (rState)
|
||||
{
|
||||
if (id == 0)
|
||||
break;
|
||||
if (rState->id == id)
|
||||
break;
|
||||
rState = rState->prevRecordedState.GetPtr();
|
||||
}
|
||||
if (!rState)
|
||||
{
|
||||
frmHeap->ReleaseLock();
|
||||
return false;
|
||||
}
|
||||
// apply state
|
||||
frmHeap->allocationHead = rState->allocationHead;
|
||||
frmHeap->allocationTail = rState->allocationTail;
|
||||
frmHeap->recordedStates = rState->prevRecordedState;
|
||||
frmHeap->ReleaseLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitializeMEMFrmHeap()
|
||||
{
|
||||
cafeExportRegister("coreinit", MEMCreateFrmHeapEx, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMDestroyFrmHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMGetAllocatableSizeForFrmHeapEx, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMiGetFreeStartForFrmHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMiGetFreeEndForFrmHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMAllocFromFrmHeapEx, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMFreeToFrmHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMFreeToFrmHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMRecordStateForFrmHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMFreeByStateToFrmHeap, LogType::CoreinitMem);
|
||||
}
|
||||
}
|
||||
41
src/Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.h
Normal file
41
src/Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct MEMFrmHeapRecordedState
|
||||
{
|
||||
uint32be id;
|
||||
MEMPTR<void> allocationHead;
|
||||
MEMPTR<void> allocationTail;
|
||||
MEMPTR<MEMFrmHeapRecordedState> prevRecordedState;
|
||||
};
|
||||
static_assert(sizeof(MEMFrmHeapRecordedState) == 0x10);
|
||||
|
||||
struct MEMFrmHeap : MEMHeapBase
|
||||
{
|
||||
/* +0x34 */ uint32be ukn34;
|
||||
/* +0x38 */ uint32be ukn38;
|
||||
/* +0x3C */ uint32be ukn3C;
|
||||
/* +0x40 */ MEMPTR<void> allocationHead;
|
||||
/* +0x44 */ MEMPTR<void> allocationTail;
|
||||
/* +0x48 */ MEMPTR<MEMFrmHeapRecordedState> recordedStates;
|
||||
};
|
||||
static_assert(sizeof(MEMFrmHeap) == 0x4C);
|
||||
|
||||
enum class FrmHeapMode : uint32
|
||||
{
|
||||
Head = (1 << 0),
|
||||
Tail = (1 << 1),
|
||||
All = (Head | Tail),
|
||||
};
|
||||
|
||||
MEMFrmHeap* MEMCreateFrmHeapEx(void* memStart, uint32 size, uint32 createFlags);
|
||||
void* MEMDestroyFrmHeap(MEMFrmHeap* frmHeap);
|
||||
|
||||
void* MEMAllocFromFrmHeapEx(MEMFrmHeap* frmHeap, uint32 size, sint32 alignment);
|
||||
void MEMFreeToFrmHeap(MEMFrmHeap* frmHeap, FrmHeapMode mode);
|
||||
|
||||
void InitializeMEMFrmHeap();
|
||||
}
|
||||
ENABLE_BITMASK_OPERATORS(coreinit::FrmHeapMode);
|
||||
146
src/Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.cpp
Normal file
146
src/Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void _MEMUnitHeap_IsValidHeap(MEMHeapHandle heap)
|
||||
{
|
||||
cemu_assert(heap != MEM_HEAP_INVALID_HANDLE);
|
||||
cemu_assert(heap->magic == MEMHeapMagic::UNIT_HEAP);
|
||||
}
|
||||
|
||||
MEMHeapBase* MEMCreateUnitHeapEx(void* memStart, uint32 heapSize, uint32 blockSize, uint32 alignment, uint32 createFlags)
|
||||
{
|
||||
cemu_assert_debug(memStart != nullptr);
|
||||
cemu_assert_debug(alignment % MIN_ALIGNMENT == 0);
|
||||
cemu_assert_debug(MIN_ALIGNMENT <= alignment && alignment <= 32);
|
||||
|
||||
uintptr_t startAddr = (uintptr_t)memStart;
|
||||
uintptr_t endAddr = startAddr + heapSize;
|
||||
|
||||
startAddr = (startAddr + MIN_ALIGNMENT_MINUS_ONE) & (~MIN_ALIGNMENT_MINUS_ONE);
|
||||
endAddr &= (~MIN_ALIGNMENT_MINUS_ONE);
|
||||
|
||||
if (startAddr > endAddr)
|
||||
return nullptr;
|
||||
|
||||
const uint32 alignmentMinusOne = alignment - 1;
|
||||
|
||||
MEMUnitHeap* unitHeap = (MEMUnitHeap*)startAddr;
|
||||
uintptr_t alignedStart = startAddr + sizeof(MEMUnitHeap) + alignmentMinusOne & ~((uintptr_t)alignmentMinusOne);
|
||||
if (alignedStart > endAddr)
|
||||
return nullptr;
|
||||
|
||||
blockSize = blockSize + alignmentMinusOne & ~alignmentMinusOne;
|
||||
uint32 totalBlockSize = (uint32)(endAddr - alignedStart);
|
||||
uint32 numBlocks = totalBlockSize / blockSize;
|
||||
if (numBlocks == 0)
|
||||
return nullptr;
|
||||
|
||||
MEMInitHeapBase(unitHeap, MEMHeapMagic::UNIT_HEAP, (void*)alignedStart, (void*)(alignedStart + (numBlocks * blockSize)), createFlags);
|
||||
|
||||
unitHeap->firstFreeBlock = (MEMUnitHeapBlock*)alignedStart;
|
||||
unitHeap->blockSize = blockSize;
|
||||
|
||||
MEMUnitHeapBlock* currentBlock = (MEMUnitHeapBlock*)alignedStart;
|
||||
for (uint32 i = 0; i < numBlocks - 1; ++i)
|
||||
{
|
||||
MEMUnitHeapBlock* nextBlock = (MEMUnitHeapBlock*)((uintptr_t)currentBlock + blockSize);
|
||||
currentBlock->nextBlock = nextBlock;
|
||||
currentBlock = nextBlock;
|
||||
}
|
||||
|
||||
currentBlock->nextBlock = nullptr;
|
||||
|
||||
if ((MEMHeapHandle*)startAddr != nullptr)
|
||||
{
|
||||
MEMHeapTable_Add((MEMHeapHandle)startAddr);
|
||||
}
|
||||
|
||||
return (MEMHeapBase*)startAddr;
|
||||
}
|
||||
|
||||
void* MEMDestroyUnitHeap(MEMHeapHandle heap)
|
||||
{
|
||||
_MEMUnitHeap_IsValidHeap(heap);
|
||||
MEMBaseDestroyHeap(heap);
|
||||
MEMHeapTable_Remove(heap);
|
||||
return heap;
|
||||
}
|
||||
|
||||
uint32 MEMCalcHeapSizeForUnitHeap(uint32 blockSize, uint32 blockCount, uint32 alignment)
|
||||
{
|
||||
uint32 alignedBlockSize = (blockSize + (alignment - 1)) & ~(alignment - 1);
|
||||
uint32 blockTotalSize = blockCount * alignedBlockSize;
|
||||
uint32 heapSize = blockTotalSize + (alignment - 4) + sizeof(MEMUnitHeap);
|
||||
return heapSize;
|
||||
}
|
||||
|
||||
uint32 MEMCountFreeBlockForUnitHeap(coreinit::MEMUnitHeap* heap)
|
||||
{
|
||||
_MEMUnitHeap_IsValidHeap(heap);
|
||||
heap->AcquireLock();
|
||||
MEMUnitHeapBlock* currentBlock = heap->firstFreeBlock;
|
||||
uint32 blockCount = 0;
|
||||
while (currentBlock)
|
||||
{
|
||||
blockCount++;
|
||||
currentBlock = currentBlock->nextBlock;
|
||||
}
|
||||
heap->ReleaseLock();
|
||||
return blockCount;
|
||||
}
|
||||
|
||||
void* MEMAllocFromUnitHeap(MEMUnitHeap* heap)
|
||||
{
|
||||
_MEMUnitHeap_IsValidHeap(heap);
|
||||
heap->AcquireLock();
|
||||
MEMUnitHeapBlock* currentBlock = heap->firstFreeBlock;
|
||||
if (!currentBlock)
|
||||
{
|
||||
heap->ReleaseLock();
|
||||
return nullptr;
|
||||
}
|
||||
// remove from list of free blocks
|
||||
heap->firstFreeBlock = currentBlock->nextBlock;
|
||||
// fill block
|
||||
if (heap->HasOptionClear())
|
||||
{
|
||||
memset(currentBlock, 0, heap->blockSize);
|
||||
}
|
||||
else if (heap->HasOptionFill())
|
||||
{
|
||||
memset(currentBlock, coreinit::MEMGetFillValForHeap(coreinit::HEAP_FILL_TYPE::ON_ALLOC), heap->blockSize);
|
||||
}
|
||||
heap->ReleaseLock();
|
||||
return currentBlock;
|
||||
}
|
||||
|
||||
void MEMFreeToUnitHeap(MEMUnitHeap* heap, void* mem)
|
||||
{
|
||||
_MEMUnitHeap_IsValidHeap(heap);
|
||||
if (!mem)
|
||||
return;
|
||||
heap->AcquireLock();
|
||||
cemu_assert_debug(mem >= heap->heapStart.GetPtr() && mem < heap->heapEnd.GetPtr());
|
||||
// add to list of free blocks
|
||||
MEMUnitHeapBlock* releasedBlock = (MEMUnitHeapBlock*)mem;
|
||||
releasedBlock->nextBlock = heap->firstFreeBlock;
|
||||
heap->firstFreeBlock = releasedBlock;
|
||||
if (heap->HasOptionFill())
|
||||
memset(releasedBlock, coreinit::MEMGetFillValForHeap(coreinit::HEAP_FILL_TYPE::ON_FREE), heap->blockSize);
|
||||
heap->ReleaseLock();
|
||||
}
|
||||
|
||||
void InitializeMEMUnitHeap()
|
||||
{
|
||||
cafeExportRegister("coreinit", MEMCreateUnitHeapEx, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMDestroyUnitHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMCalcHeapSizeForUnitHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMCountFreeBlockForUnitHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMAllocFromUnitHeap, LogType::CoreinitMem);
|
||||
cafeExportRegister("coreinit", MEMFreeToUnitHeap, LogType::CoreinitMem);
|
||||
}
|
||||
}
|
||||
24
src/Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.h
Normal file
24
src/Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct MEMUnitHeapBlock
|
||||
{
|
||||
MEMPTR<MEMUnitHeapBlock> nextBlock;
|
||||
};
|
||||
static_assert(sizeof(MEMUnitHeapBlock) == 4);
|
||||
|
||||
struct MEMUnitHeap : public MEMHeapBase
|
||||
{
|
||||
/* +0x34 */ uint32 padding034;
|
||||
/* +0x38 */ uint32 padding038;
|
||||
/* +0x3C */ uint32 padding03C;
|
||||
/* +0x40 */ MEMPTR<MEMUnitHeapBlock> firstFreeBlock;
|
||||
/* +0x44 */ uint32be blockSize;
|
||||
};
|
||||
static_assert(sizeof(MEMUnitHeap) == 0x48);
|
||||
|
||||
MEMHeapBase* MEMCreateUnitHeapEx(void* memStart, uint32 heapSize, uint32 memBlockSize, uint32 alignment, uint32 createFlags);
|
||||
void* MEMDestroyUnitHeap(MEMHeapHandle hHeap);
|
||||
}
|
||||
516
src/Cafe/OS/libs/coreinit/coreinit_MPQueue.cpp
Normal file
516
src/Cafe/OS/libs/coreinit/coreinit_MPQueue.cpp
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MPQueue.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/HW/Espresso/PPCCallback.h"
|
||||
#include "util/helpers/fspinlock.h"
|
||||
|
||||
// titles that utilize MP task queue: Yoshi's Woolly World, Fast Racing Neo, Tokyo Mirage Sessions, Mii Maker
|
||||
|
||||
#define AcquireMPQLock() s_workaroundSpinlock.acquire()
|
||||
#define ReleaseMPQLock() s_workaroundSpinlock.release()
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
FSpinlock s_workaroundSpinlock; // workaround for a race condition
|
||||
/*
|
||||
Race condition pseudo-code:
|
||||
|
||||
WorkerThreads:
|
||||
while( task = MPDequeTask() ) MPRunTask(task);
|
||||
|
||||
MainThread:
|
||||
QueueTasks();
|
||||
// wait and reset
|
||||
MPWaitForTaskQWithTimeout(DONE)
|
||||
MPTermTaskQ()
|
||||
MPInitTaskQ()
|
||||
|
||||
The race condition then happens when a worker thread calls MPDequeTask()/MPRunTask while MPInitTaskQ() is being executed on the main thread.
|
||||
Since MPInitTaskQ() (re)initializes the internal spinlock it's not thread-safe, leading to a corruption of the spinlock state for other threads
|
||||
|
||||
We work around this by using a global spinlock instead of the taskQ specific one
|
||||
*/
|
||||
|
||||
|
||||
void MPInitTask(MPTask* task, void* func, void* data, uint32 size)
|
||||
{
|
||||
s_workaroundSpinlock.acquire();
|
||||
task->thisptr = task;
|
||||
|
||||
task->coreIndex = PPC_CORE_COUNT;
|
||||
|
||||
task->taskFunc.func = func;
|
||||
task->taskFunc.data = data;
|
||||
task->taskFunc.size = size;
|
||||
|
||||
task->taskState = MP_TASK_STATE_INIT;
|
||||
|
||||
task->userdata = nullptr;
|
||||
task->runtime = 0;
|
||||
s_workaroundSpinlock.release();
|
||||
}
|
||||
|
||||
bool MPTermTask(MPTask* task)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MPRunTask(MPTask* task)
|
||||
{
|
||||
if (task->taskState != MP_TASK_STATE_READY)
|
||||
return false;
|
||||
|
||||
auto* taskQ = task->taskQ.GetPtr();
|
||||
|
||||
if(taskQ->state != MP_TASKQ_STATE_STOPPING && taskQ->state != MP_TASKQ_STATE_STOP)
|
||||
{
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskQ->spinlock);
|
||||
if (taskQ->state == MP_TASKQ_STATE_STOPPING || taskQ->state == MP_TASKQ_STATE_STOP)
|
||||
{
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskQ->spinlock);
|
||||
return false;
|
||||
}
|
||||
|
||||
taskQ->taskReadyCount = taskQ->taskReadyCount - 1;
|
||||
taskQ->taskRunCount = taskQ->taskRunCount + 1;
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskQ->spinlock);
|
||||
|
||||
const auto startTime = OSGetSystemTime();
|
||||
|
||||
task->coreIndex = OSGetCoreId();
|
||||
task->taskState = MP_TASK_STATE_RUN;
|
||||
|
||||
task->taskFunc.result = PPCCoreCallback(task->taskFunc.func, task->taskFunc.data, task->taskFunc.size);
|
||||
|
||||
task->taskState = MP_TASK_STATE_DONE;
|
||||
|
||||
const auto endTime = OSGetSystemTime();
|
||||
task->runtime = endTime - startTime;
|
||||
|
||||
cemu_assert_debug(taskQ->state != MP_TASKQ_STATE_DONE);
|
||||
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskQ->spinlock);
|
||||
taskQ->taskRunCount = taskQ->taskRunCount - 1;
|
||||
taskQ->taskDoneCount[1] = taskQ->taskDoneCount[1] + 1;
|
||||
if (taskQ->state == MP_TASKQ_STATE_STOPPING && taskQ->taskRunCount == 0)
|
||||
taskQ->state = MP_TASKQ_STATE_STOP;
|
||||
|
||||
if (taskQ->taskCount == taskQ->taskDoneCount[1])
|
||||
taskQ->state = MP_TASKQ_STATE_DONE;
|
||||
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskQ->spinlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MPGetTaskInfo(MPTask* task, MPTaskInfo* info)
|
||||
{
|
||||
info->state = task->taskState;
|
||||
info->coreIndex = task->coreIndex;
|
||||
info->runtime = task->runtime;
|
||||
// not setting function result?
|
||||
return true;
|
||||
}
|
||||
|
||||
void* MPGetTaskUserData(MPTask* task)
|
||||
{
|
||||
return task->userdata.GetPtr();
|
||||
}
|
||||
|
||||
void MPSetTaskUserData(MPTask* task, void* userdata)
|
||||
{
|
||||
task->userdata = userdata;
|
||||
}
|
||||
|
||||
void MPInitTaskQ(MPTaskQ* taskQ, MPTask** tasks, uint32 taskCount)
|
||||
{
|
||||
AcquireMPQLock();
|
||||
taskQ->thisptr = taskQ;
|
||||
OSInitSpinLock(&taskQ->spinlock);
|
||||
taskQ->state = MP_TASKQ_STATE_INIT;
|
||||
taskQ->taskReadyCount = 0;
|
||||
taskQ->taskCount = 0;
|
||||
taskQ->taskRunCount = 0;
|
||||
for(uint32 i = 0; i < OSGetCoreCount(); ++i)
|
||||
{
|
||||
taskQ->taskDoneCount[i] = 0;
|
||||
taskQ->nextIndex[i] = 0;
|
||||
taskQ->endIndex[i] = 0;
|
||||
}
|
||||
|
||||
taskQ->taskQueue = (MEMPTR<MPTask>*)tasks;
|
||||
taskQ->taskQueueSize = taskCount;
|
||||
|
||||
ReleaseMPQLock();
|
||||
}
|
||||
|
||||
bool MPEnqueTask(MPTaskQ* taskq, MPTask* task)
|
||||
{
|
||||
bool result = false;
|
||||
if(task->taskState == MP_TASK_STATE_INIT)
|
||||
{
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
|
||||
const uint32 taskQState = taskq->state;
|
||||
if((uint32)taskq->endIndex[1] < taskq->taskQueueSize
|
||||
&& (taskQState == MP_TASKQ_STATE_INIT || taskQState == MP_TASKQ_STATE_RUN || taskQState == MP_TASKQ_STATE_STOPPING || taskQState == MP_TASKQ_STATE_STOP || taskQState == MP_TASKQ_STATE_DONE))
|
||||
{
|
||||
task->taskQ = taskq;
|
||||
task->taskState = MP_TASK_STATE_READY;
|
||||
|
||||
taskq->thisptr = taskq;
|
||||
|
||||
const uint32 endIndex = taskq->endIndex[1];
|
||||
taskq->endIndex[1] = endIndex + 1;
|
||||
|
||||
taskq->taskCount = taskq->taskCount + 1;
|
||||
taskq->taskReadyCount = taskq->taskReadyCount + 1;
|
||||
taskq->taskQueue[endIndex] = task;
|
||||
|
||||
if (taskQState == MP_TASKQ_STATE_DONE)
|
||||
taskq->state = MP_TASKQ_STATE_RUN;
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MPTermTaskQ(MPTaskQ* taskq)
|
||||
{
|
||||
// workaround code for TMS
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
//if ((uint32)taskq->taskReadyCount > 0 && taskq->state == MP_TASKQ_STATE_RUN)
|
||||
if (taskq->state == MP_TASKQ_STATE_RUN)
|
||||
{
|
||||
taskq->state = MP_TASKQ_STATE_STOP;
|
||||
}
|
||||
|
||||
while (taskq->taskRunCount != 0)
|
||||
{
|
||||
// wait for tasks to finish
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
OSYieldThread();
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
}
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MPGetTaskQInfo(MPTaskQ* taskq, MPTaskQInfo* info)
|
||||
{
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
info->state = taskq->state;
|
||||
info->taskCount = taskq->taskCount;
|
||||
info->taskReadyCount = taskq->taskReadyCount;
|
||||
info->taskRunCount = taskq->taskRunCount;
|
||||
info->taskDoneCount = taskq->taskDoneCount[1];
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MPStartTaskQ(MPTaskQ* taskq)
|
||||
{
|
||||
bool result = false;
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
if (taskq->state == MP_TASKQ_STATE_INIT || taskq->state == MP_TASKQ_STATE_STOP)
|
||||
{
|
||||
taskq->state = MP_TASKQ_STATE_RUN;
|
||||
result = true;
|
||||
}
|
||||
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MPRunTasksFromTaskQ(MPTaskQ* taskq, int granularity)
|
||||
{
|
||||
uint32 result = 0;
|
||||
while (true)
|
||||
{
|
||||
if (taskq->state != MP_TASKQ_STATE_RUN)
|
||||
return (taskq->state & MP_TASKQ_STATE_DONE) != 0;
|
||||
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
const auto nextIndex = taskq->nextIndex[1];
|
||||
const auto endIndex = taskq->endIndex[1];
|
||||
if (nextIndex == endIndex)
|
||||
break;
|
||||
|
||||
auto newNextIndex = nextIndex + granularity;
|
||||
if (endIndex < nextIndex + granularity)
|
||||
newNextIndex = endIndex;
|
||||
|
||||
const auto workCount = (newNextIndex - nextIndex);
|
||||
|
||||
taskq->nextIndex[1] = newNextIndex;
|
||||
taskq->taskReadyCount = taskq->taskReadyCount - workCount;
|
||||
taskq->taskRunCount = taskq->taskRunCount + workCount;
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
|
||||
// since we are having a granularity parameter, we might want to give the scheduler the chance for other stuff when having multiple tasks
|
||||
if(result != 0)
|
||||
PPCCore_switchToScheduler();
|
||||
|
||||
for (int i = nextIndex; i < newNextIndex; ++i)
|
||||
{
|
||||
const auto startTime = OSGetSystemTime();
|
||||
|
||||
const auto& task = taskq->taskQueue[i];
|
||||
result = task->thisptr.GetMPTR();
|
||||
|
||||
task->taskState = MP_TASK_STATE_RUN;
|
||||
task->coreIndex = OSGetCoreId();
|
||||
|
||||
task->taskFunc.result = PPCCoreCallback(task->taskFunc.func, task->taskFunc.data, task->taskFunc.size);
|
||||
|
||||
task->taskState = MP_TASK_STATE_DONE;
|
||||
|
||||
const auto endTime = OSGetSystemTime();
|
||||
task->runtime = endTime - startTime;
|
||||
}
|
||||
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
const auto runRemaining = taskq->taskRunCount - workCount;
|
||||
taskq->taskRunCount = runRemaining;
|
||||
|
||||
const auto doneCount = taskq->taskDoneCount[1] + workCount;
|
||||
taskq->taskDoneCount[1] = doneCount;
|
||||
|
||||
if (taskq->state == 4 && runRemaining == 0)
|
||||
taskq->state = MP_TASKQ_STATE_STOP;
|
||||
|
||||
if (taskq->taskCount == doneCount)
|
||||
taskq->state = MP_TASKQ_STATE_DONE;
|
||||
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
}
|
||||
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
bool MPStopTaskQ(MPTaskQ* taskq)
|
||||
{
|
||||
bool result = false;
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
if (taskq->state == MP_TASKQ_STATE_RUN)
|
||||
{
|
||||
taskq->state = MP_TASKQ_STATE_STOPPING;
|
||||
if (taskq->taskRunCount == 0)
|
||||
taskq->state = MP_TASKQ_STATE_STOP;
|
||||
|
||||
result = true;
|
||||
}
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MPWaitTaskQ(MPTaskQ* taskQ, uint32 waitState)
|
||||
{
|
||||
bool waitRun = (waitState & MP_TASKQ_STATE_RUN) != 0;
|
||||
bool waitStop = (waitState & MP_TASKQ_STATE_STOP) != 0;
|
||||
bool waitDone = (waitState & MP_TASKQ_STATE_DONE) != 0;
|
||||
|
||||
size_t loopCounter = 0;
|
||||
while (waitStop || waitDone || waitRun)
|
||||
{
|
||||
const uint32 state = taskQ->state;
|
||||
if (waitRun && HAS_FLAG(state, MP_TASKQ_STATE_RUN))
|
||||
{
|
||||
waitRun = false;
|
||||
waitDone = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (waitStop && HAS_FLAG(state, MP_TASKQ_STATE_STOP))
|
||||
{
|
||||
waitStop = false;
|
||||
waitDone = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (waitDone && HAS_FLAG(state, MP_TASKQ_STATE_DONE))
|
||||
{
|
||||
waitDone = false;
|
||||
waitRun = false;
|
||||
waitStop = false;
|
||||
continue;
|
||||
}
|
||||
if (loopCounter > 0)
|
||||
coreinit::OSSleepTicks(EspressoTime::ConvertNsToTimerTicks(50000)); // sleep thread for 0.05ms to give other threads a chance to run (avoids softlocks in YWW)
|
||||
else
|
||||
PPCCore_switchToScheduler();
|
||||
loopCounter++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MPWaitTaskQWithTimeout(MPTaskQ* taskQ, uint32 waitState, sint64 timeout)
|
||||
{
|
||||
bool waitRun = (waitState & MP_TASKQ_STATE_RUN) != 0;
|
||||
bool waitStop = (waitState & MP_TASKQ_STATE_STOP) != 0;
|
||||
bool waitDone = (waitState & MP_TASKQ_STATE_DONE) != 0;
|
||||
|
||||
const auto startTime = OSGetSystemTime();
|
||||
const auto timerTicks = EspressoTime::ConvertNsToTimerTicks(timeout);
|
||||
const auto endTime = startTime + timerTicks;
|
||||
|
||||
while (waitStop || waitDone || waitRun)
|
||||
{
|
||||
const uint32 state = taskQ->state;
|
||||
if (waitRun && HAS_FLAG(state, MP_TASKQ_STATE_RUN))
|
||||
{
|
||||
waitRun = false;
|
||||
waitDone = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (waitStop && HAS_FLAG(state, MP_TASKQ_STATE_STOP))
|
||||
{
|
||||
waitStop = false;
|
||||
waitDone = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (waitDone && HAS_FLAG(state, MP_TASKQ_STATE_DONE))
|
||||
{
|
||||
waitDone = false;
|
||||
waitRun = false;
|
||||
waitStop = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OSGetSystemTime() >= endTime)
|
||||
{
|
||||
if (waitState == MP_TASKQ_STATE_DONE)
|
||||
cemuLog_log(LogType::Force, "MPWaitTaskQWithTimeout(): Timeout occurred while waiting for done-only state");
|
||||
return false;
|
||||
}
|
||||
|
||||
PPCCore_switchToScheduler();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MPTask* MPDequeTask(MPTaskQ* taskq)
|
||||
{
|
||||
MPTask* result = nullptr;
|
||||
if (taskq->state == MP_TASKQ_STATE_RUN)
|
||||
{
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
if (taskq->state == MP_TASKQ_STATE_RUN && taskq->nextIndex[1] != taskq->endIndex[1])
|
||||
{
|
||||
result = taskq->taskQueue[taskq->nextIndex[1]].GetPtr();
|
||||
taskq->nextIndex[1] = taskq->nextIndex[1] + 1;
|
||||
}
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 MPDequeTasks(MPTaskQ* taskq, MPTask** tasks, sint32 maxTasks)
|
||||
{
|
||||
uint32 dequeCount = 0;
|
||||
if (taskq->state == MP_TASKQ_STATE_RUN)
|
||||
{
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
if (taskq->state == MP_TASKQ_STATE_RUN)
|
||||
{
|
||||
auto nextIndex = (sint32)taskq->nextIndex[1];
|
||||
auto newEndIndex = nextIndex + maxTasks;
|
||||
if (taskq->endIndex[1] < nextIndex + maxTasks)
|
||||
newEndIndex = taskq->endIndex[1];
|
||||
|
||||
dequeCount = newEndIndex - nextIndex;
|
||||
taskq->nextIndex[1] = newEndIndex;
|
||||
|
||||
for(int i = 0; nextIndex < newEndIndex; ++nextIndex, ++i)
|
||||
{
|
||||
tasks[i] = taskq->taskQueue[nextIndex].GetPtr();
|
||||
}
|
||||
|
||||
auto idx = 0;
|
||||
while (nextIndex < newEndIndex)
|
||||
{
|
||||
tasks[idx] = taskq->taskQueue[nextIndex].GetPtr();
|
||||
nextIndex = nextIndex + 1;
|
||||
idx = idx + 1;
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
}
|
||||
return dequeCount;
|
||||
}
|
||||
|
||||
bool MPResetTaskQ(MPTaskQ* taskq)
|
||||
{
|
||||
debug_printf("MPResetTaskQ called\n");
|
||||
bool result = false;
|
||||
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
|
||||
if (taskq->state == MP_TASKQ_STATE_DONE || taskq->state == MP_TASKQ_STATE_STOP)
|
||||
{
|
||||
taskq->state = MP_TASKQ_STATE_INIT;
|
||||
taskq->taskRunCount = 0;
|
||||
taskq->taskCount = taskq->endIndex[1];
|
||||
taskq->taskReadyCount = taskq->endIndex[1];
|
||||
|
||||
for(uint32 i = 0; i < OSGetCoreCount(); ++i)
|
||||
{
|
||||
taskq->taskDoneCount[i] = 0;
|
||||
taskq->nextIndex[i] = 0;
|
||||
}
|
||||
|
||||
for(uint32 i = 0; i < taskq->taskCount; ++i)
|
||||
{
|
||||
const auto& task = taskq->taskQueue[i];
|
||||
task->taskFunc.result = 0;
|
||||
|
||||
task->coreIndex = PPC_CORE_COUNT;
|
||||
task->runtime = 0;
|
||||
task->taskState = MP_TASK_STATE_READY;
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
|
||||
return result;
|
||||
}
|
||||
|
||||
void InitializeMP()
|
||||
{
|
||||
// task
|
||||
cafeExportRegister("coreinit", MPInitTask, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPTermTask, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPRunTask, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPGetTaskInfo, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPGetTaskUserData, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPSetTaskUserData, LogType::CoreinitMP);
|
||||
|
||||
// taskq
|
||||
cafeExportRegister("coreinit", MPInitTaskQ, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPResetTaskQ, LogType::CoreinitMP);
|
||||
|
||||
cafeExportRegister("coreinit", MPEnqueTask, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPDequeTask, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPDequeTasks, LogType::CoreinitMP);
|
||||
|
||||
cafeExportRegister("coreinit", MPRunTasksFromTaskQ, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPStartTaskQ, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPWaitTaskQ, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPWaitTaskQWithTimeout, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPStopTaskQ, LogType::CoreinitMP);
|
||||
|
||||
cafeExportRegister("coreinit", MPTermTaskQ, LogType::CoreinitMP);
|
||||
cafeExportRegister("coreinit", MPGetTaskQInfo, LogType::CoreinitMP);
|
||||
}
|
||||
}
|
||||
106
src/Cafe/OS/libs/coreinit/coreinit_MPQueue.h
Normal file
106
src/Cafe/OS/libs/coreinit/coreinit_MPQueue.h
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
enum MPTaskState
|
||||
{
|
||||
MP_TASK_STATE_INIT = (1 << 0),
|
||||
MP_TASK_STATE_READY = (1 << 1),
|
||||
MP_TASK_STATE_RUN = (1 << 2),
|
||||
MP_TASK_STATE_DONE = (1 << 3)
|
||||
};
|
||||
|
||||
enum MPTaskQState
|
||||
{
|
||||
MP_TASKQ_STATE_INIT = (1 << 0),
|
||||
MP_TASKQ_STATE_RUN = (1 << 1),
|
||||
MP_TASKQ_STATE_STOPPING = (1 << 2),
|
||||
MP_TASKQ_STATE_STOP = (1 << 3),
|
||||
MP_TASKQ_STATE_DONE = (1 << 4)
|
||||
};
|
||||
|
||||
struct MPTaskFunction
|
||||
{
|
||||
/* +0x00 */ MEMPTR<void> func;
|
||||
/* +0x04 */ MEMPTR<void> data;
|
||||
/* +0x08 */ uint32be size;
|
||||
/* +0x0C */ uint32be result;
|
||||
};
|
||||
static_assert(sizeof(MPTaskFunction) == 0x10);
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct MPTask
|
||||
{
|
||||
/* +0x00 */ MEMPTR<void> thisptr;
|
||||
/* +0x04 */ MEMPTR<struct MPTaskQ> taskQ;
|
||||
/* +0x08 */ uint32be taskState;
|
||||
/* +0x0C */ MPTaskFunction taskFunc;
|
||||
/* +0x1C */ uint32be coreIndex;
|
||||
/* +0x20 */ sint64be runtime;
|
||||
/* +0x28 */ MEMPTR<void> userdata;
|
||||
};
|
||||
static_assert(sizeof(MPTask) == 0x2C);
|
||||
|
||||
#pragma pack()
|
||||
|
||||
struct MPTaskQ
|
||||
{
|
||||
/* +0x00 */ MEMPTR<void> thisptr;
|
||||
/* +0x04 */ uint32be state;
|
||||
/* +0x08 */ uint32be taskCount;
|
||||
/* +0x0C */ uint32be taskReadyCount;
|
||||
/* +0x10 */ uint32be taskRunCount;
|
||||
/* +0x14 */ uint32be taskDoneCount[PPC_CORE_COUNT];
|
||||
/* +0x20 */ sint32be nextIndex[PPC_CORE_COUNT];
|
||||
/* +0x2C */ sint32be endIndex[PPC_CORE_COUNT];
|
||||
/* +0x38 */ MEMPTR<MEMPTR<MPTask>> taskQueue;
|
||||
/* +0x3C */ uint32be taskQueueSize;
|
||||
/* +0x40 */ OSSpinLock spinlock;
|
||||
};
|
||||
static_assert(sizeof(MPTaskQ) == 0x50);
|
||||
|
||||
struct MPTaskQInfo
|
||||
{
|
||||
/* +0x00 */ uint32be state;
|
||||
/* +0x04 */ uint32be taskCount;
|
||||
/* +0x08 */ uint32be taskReadyCount;
|
||||
/* +0x0C */ uint32be taskRunCount;
|
||||
/* +0x10 */ uint32be taskDoneCount;
|
||||
};
|
||||
static_assert(sizeof(MPTaskQInfo) == 0x14);
|
||||
|
||||
struct MPTaskInfo
|
||||
{
|
||||
/* +0x00 */ uint32be state;
|
||||
/* +0x04 */ uint32be funcResult;
|
||||
/* +0x08 */ uint32be coreIndex;
|
||||
/* +0x0C */ sint64be runtime;
|
||||
};
|
||||
static_assert(sizeof(MPTaskQInfo) == 0x14);
|
||||
|
||||
void MPInitTask(MPTask* task, void* func, void* data, uint32 size);
|
||||
bool MPTermTask(MPTask* task);
|
||||
bool MPRunTask(MPTask* task);
|
||||
bool MPGetTaskInfo(MPTask* task, MPTaskInfo* info);
|
||||
void* MPGetTaskUserData(MPTask* task);
|
||||
void MPSetTaskUserData(MPTask* task, void* userdata);
|
||||
|
||||
void MPInitTaskQ(MPTaskQ* taskq, MPTask** tasks, uint32 taskCount);
|
||||
bool MPEnqueTask(MPTaskQ* taskq, MPTask* task);
|
||||
bool MPTermTaskQ(MPTaskQ* taskq);
|
||||
bool MPGetTaskQInfo(MPTaskQ* taskq, MPTaskQInfo* info);
|
||||
bool MPStartTaskQ(MPTaskQ* taskq);
|
||||
bool MPRunTasksFromTaskQ(MPTaskQ* taskq, int granularity);
|
||||
bool MPStopTaskQ(MPTaskQ* taskq);
|
||||
bool MPWaitTaskQ(MPTaskQ* taskq, uint32 waitState);
|
||||
bool MPWaitTaskQWithTimeout(MPTaskQ* taskq, uint32 waitState, sint64 timeout);
|
||||
MPTask* MPDequeTask(MPTaskQ* taskq);
|
||||
uint32 MPDequeTasks(MPTaskQ* taskq, MPTask** tasks, sint32 maxTasks);
|
||||
bool MPResetTaskQ(MPTaskQ* taskq);
|
||||
|
||||
void InitializeMP();
|
||||
}
|
||||
225
src/Cafe/OS/libs/coreinit/coreinit_Memory.cpp
Normal file
225
src/Cafe/OS/libs/coreinit/coreinit_Memory.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "coreinit_Memory.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
#include "Cafe/GraphicPack/GraphicPack2.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
void DCInvalidateRange(MPTR addr, uint32 size)
|
||||
{
|
||||
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
|
||||
addr &= ~0x1F;
|
||||
//LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
|
||||
}
|
||||
|
||||
void DCFlushRange(MPTR addr, uint32 size)
|
||||
{
|
||||
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
|
||||
addr &= ~0x1F;
|
||||
LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
|
||||
}
|
||||
|
||||
void DCFlushRangeNoSync(MPTR addr, uint32 size)
|
||||
{
|
||||
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
|
||||
addr &= ~0x1F;
|
||||
LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
|
||||
}
|
||||
|
||||
void DCStoreRange(MPTR addr, uint32 size)
|
||||
{
|
||||
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
|
||||
addr &= ~0x1F;
|
||||
//LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
|
||||
}
|
||||
|
||||
void DCStoreRangeNoSync(MPTR addr, uint32 size)
|
||||
{
|
||||
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
|
||||
addr &= ~0x1F;
|
||||
LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
|
||||
}
|
||||
|
||||
void DCZeroRange(MPTR addr, uint32 size)
|
||||
{
|
||||
MPTR alignedAddr = addr & ~31;
|
||||
uint32 cachlineOffset = addr & 31;
|
||||
uint32 blocks = (cachlineOffset + size + 31) / 32;
|
||||
|
||||
if (blocks > 0)
|
||||
{
|
||||
memset(memory_getPointerFromVirtualOffset(alignedAddr), 0x00, blocks * 32);
|
||||
LatteBufferCache_notifyDCFlush(alignedAddr, blocks * 32);
|
||||
}
|
||||
}
|
||||
|
||||
bool OSIsAddressRangeDCValid(uint32 startOffset, uint32 range)
|
||||
{
|
||||
uint32 endOffset = startOffset + range - 1;
|
||||
uint32 boundaryLow = 0xE8000000;
|
||||
uint32 boundaryHigh = 0xEC000000;
|
||||
if (startOffset < boundaryLow || startOffset >= boundaryHigh)
|
||||
return false;
|
||||
if (endOffset < boundaryLow || endOffset >= boundaryHigh)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void* coreinit_memset(void* dst, uint32 value, uint32 size)
|
||||
{
|
||||
memset(dst, value, size);
|
||||
return dst;
|
||||
}
|
||||
|
||||
void* coreinit_memcpy(MEMPTR<void> dst, MEMPTR<void> src, uint32 size)
|
||||
{
|
||||
if (dst.GetMPTR() == 0xFFFFFFFF)
|
||||
{
|
||||
// games this was seen in: The Swapper
|
||||
// this may be a bug in the game. The last few bytes of the address space are writable, but wrap-around behavior of COS memcpy is unknown
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
if (size > 0)
|
||||
{
|
||||
memcpy(dst.GetPtr(), src.GetPtr(), size);
|
||||
// always flushes the cache!
|
||||
LatteBufferCache_notifyDCFlush(dst.GetMPTR(), size);
|
||||
}
|
||||
|
||||
return dst.GetPtr();
|
||||
}
|
||||
|
||||
void* coreinit_memmove(MEMPTR<void> dst, void* src, uint32 size)
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
memmove(dst.GetPtr(), src, size);
|
||||
// always flushes the cache!
|
||||
LatteBufferCache_notifyDCFlush(dst.GetMPTR(), size);
|
||||
}
|
||||
return dst.GetPtr();
|
||||
}
|
||||
|
||||
void* OSBlockMove(MEMPTR<void> dst, MEMPTR<void> src, uint32 size, bool flushDC)
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
memmove(dst.GetPtr(), src.GetPtr(), size);
|
||||
if (flushDC)
|
||||
LatteBufferCache_notifyDCFlush(dst.GetMPTR(), size);
|
||||
}
|
||||
return dst.GetPtr();
|
||||
}
|
||||
|
||||
void* OSBlockSet(MEMPTR<void> dst, uint32 value, uint32 size)
|
||||
{
|
||||
memset(dst.GetPtr(), value&0xFF, size);
|
||||
return dst.GetPtr();
|
||||
}
|
||||
|
||||
MPTR OSEffectiveToPhysical(MPTR effectiveAddr)
|
||||
{
|
||||
MPTR physicalAddr = memory_virtualToPhysical(effectiveAddr);
|
||||
return physicalAddr;
|
||||
}
|
||||
|
||||
void OSMemoryBarrier(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
void OSGetMemBound(sint32 memType, MPTR* offsetOutput, uint32* sizeOutput)
|
||||
{
|
||||
MPTR memAddr = MPTR_NULL;
|
||||
uint32 memSize = 0;
|
||||
|
||||
/*
|
||||
Data taken from browser dump:
|
||||
type start size
|
||||
MEM1 0xF4000000 0x02000000
|
||||
MEM2 0x106DE000 0x170C2000
|
||||
*/
|
||||
if (memType == 1)
|
||||
{
|
||||
// MEM1
|
||||
memAddr = mmuRange_MEM1.getBase();
|
||||
memSize = mmuRange_MEM1.getSize();
|
||||
}
|
||||
else if (memType == 2)
|
||||
{
|
||||
// MEM2
|
||||
uint32 currentRPLAllocatorOffset = RPLLoader_GetDataAllocatorAddr();
|
||||
|
||||
// due to differences in our library implementations we currently allocate less memory for the OS/RPLs than on the actual hardware,
|
||||
// as a result more memory is available to games
|
||||
// however, some games crash due to internal overflows if there is too much memory available
|
||||
|
||||
// here we artificially reduce the available memory for the affected games
|
||||
uint64 titleId = CafeSystem::GetForegroundTitleId();
|
||||
if (
|
||||
titleId == 0x0005000010132400ULL || // Lego Marvel Super Heroes (EU)
|
||||
titleId == 0x0005000010132B00ULL || // Lego Marvel Super Heroes (US)
|
||||
titleId == 0x0005000010194200ull || // Lego Dimensions (US)
|
||||
titleId == 0x0005000010195D00ull || // Lego Dimensions (EU)
|
||||
titleId == 0x00050000101A6200ull || // Lego Jurassic World (US)
|
||||
titleId == 0x00050000101A5C00 || // Lego Jurassic World (EU)
|
||||
titleId == 0x000500001014DE00 || // The Lego Movie Videogame (US)
|
||||
titleId == 0x000500001014E000 || // The Lego Movie Videogame (EU)
|
||||
titleId == 0x0005000010168D00 || // Lego The Hobbit (EU)
|
||||
titleId == 0x000500001016A700 || // Lego The Hobbit (JP)
|
||||
// The Hobbit US title id?
|
||||
titleId == 0x00050000101DAB00 || // Lego Star Wars: The Force Awakens (US)
|
||||
titleId == 0x00050000101DAA00 || // Lego Star Wars: The Force Awakens (EU)
|
||||
// LEGO Batman 3: BEYOND GOTHAM
|
||||
titleId == 0x000500001016A400 || // EU
|
||||
titleId == 0x000500001016AD00 || // US
|
||||
// Lego Marvel Avengers
|
||||
titleId == 0x00050000101BE900 || // EU
|
||||
titleId == 0x00050000101BEF00 || // US
|
||||
// LEGO BATMAN 2: DC Super Heroes
|
||||
titleId == 0x0005000010135500 || // EU
|
||||
titleId == 0x0005000010135E00 // US
|
||||
)
|
||||
{
|
||||
forceLogDebug_printf("Hack: Reduce available memory to simulate loaded RPLs");
|
||||
currentRPLAllocatorOffset += (48 * 1024 * 1024); // 48MB
|
||||
}
|
||||
memAddr = currentRPLAllocatorOffset;
|
||||
memSize = mmuRange_MEM2.getEnd() - currentRPLAllocatorOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
if (offsetOutput)
|
||||
*offsetOutput = _swapEndianU32(memAddr);
|
||||
if (sizeOutput)
|
||||
*sizeOutput = _swapEndianU32(memSize);
|
||||
}
|
||||
|
||||
void InitializeMemory()
|
||||
{
|
||||
cafeExportRegister("coreinit", DCInvalidateRange, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", DCFlushRange, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", DCFlushRangeNoSync, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", DCStoreRange, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", DCStoreRangeNoSync, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", DCZeroRange, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSIsAddressRangeDCValid, LogType::Placeholder);
|
||||
|
||||
cafeExportRegisterFunc(coreinit_memcpy, "coreinit", "memcpy", LogType::Placeholder);
|
||||
cafeExportRegisterFunc(coreinit_memset, "coreinit", "memset", LogType::Placeholder);
|
||||
cafeExportRegisterFunc(coreinit_memmove, "coreinit", "memmove", LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSBlockMove, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSBlockSet, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", OSEffectiveToPhysical, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSMemoryBarrier, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", OSGetMemBound, LogType::Placeholder);
|
||||
}
|
||||
|
||||
}
|
||||
8
src/Cafe/OS/libs/coreinit/coreinit_Memory.h
Normal file
8
src/Cafe/OS/libs/coreinit/coreinit_Memory.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitializeMemory();
|
||||
|
||||
void OSGetMemBound(sint32 memType, MPTR* offsetOutput, uint32* sizeOutput);
|
||||
}
|
||||
168
src/Cafe/OS/libs/coreinit/coreinit_MemoryMapping.cpp
Normal file
168
src/Cafe/OS/libs/coreinit/coreinit_MemoryMapping.cpp
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "util/MemMapper/MemMapper.h"
|
||||
|
||||
#define OS_MAP_READ_ONLY (1)
|
||||
#define OS_MAP_READ_WRITE (2)
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
struct OSVirtMemory
|
||||
{
|
||||
MPTR virtualAddress;
|
||||
uint32 size;
|
||||
uint32 alignment;
|
||||
OSVirtMemory* next;
|
||||
};
|
||||
|
||||
OSVirtMemory* virtualMemoryList = nullptr;
|
||||
|
||||
MPTR _VirtualMemoryAlloc(uint32 size, uint32 alignment)
|
||||
{
|
||||
uint32 currentAddress = MEMORY_MAPABLE_VIRT_AREA_OFFSET;
|
||||
uint32 endAddress = MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE;
|
||||
uint32 pageSize = (uint32)MemMapper::GetPageSize();
|
||||
while (true)
|
||||
{
|
||||
// calculated aligned start and end address for current region
|
||||
currentAddress = (currentAddress + alignment - 1) & ~(alignment - 1);
|
||||
currentAddress = (currentAddress + pageSize - 1) & ~(pageSize - 1);
|
||||
uint32 currentEndAddress = currentAddress + size;
|
||||
currentEndAddress = (currentEndAddress + pageSize - 1) & ~(pageSize - 1);
|
||||
// check if out of available space
|
||||
if (currentEndAddress >= endAddress)
|
||||
{
|
||||
debug_printf("coreinitVirtualMemory_alloc(): Unable to allocate memory\n");
|
||||
debugBreakpoint();
|
||||
return NULL;
|
||||
}
|
||||
// check for overlapping regions
|
||||
OSVirtMemory* virtMemItr = virtualMemoryList;
|
||||
bool emptySpaceFound = true;
|
||||
while (virtMemItr)
|
||||
{
|
||||
// check for range collision
|
||||
if (currentAddress < (virtMemItr->virtualAddress + virtMemItr->size) && currentEndAddress > virtMemItr->virtualAddress)
|
||||
{
|
||||
// regions overlap
|
||||
// adjust current address and try again
|
||||
currentAddress = virtMemItr->virtualAddress + virtMemItr->size;
|
||||
emptySpaceFound = false;
|
||||
break;
|
||||
}
|
||||
// next
|
||||
virtMemItr = virtMemItr->next;
|
||||
}
|
||||
if (emptySpaceFound)
|
||||
{
|
||||
// add entry
|
||||
OSVirtMemory* virtMemory = (OSVirtMemory*)malloc(sizeof(OSVirtMemory));
|
||||
memset(virtMemory, 0x00, sizeof(OSVirtMemory));
|
||||
virtMemory->virtualAddress = currentAddress;
|
||||
virtMemory->size = currentEndAddress - currentAddress;
|
||||
virtMemory->alignment = alignment;
|
||||
virtMemory->next = virtualMemoryList;
|
||||
virtualMemoryList = virtMemory;
|
||||
return currentAddress;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void coreinitExport_OSGetAvailPhysAddrRange(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 MPTR* areaStart
|
||||
// r4 uint32 areaSize
|
||||
memory_writeU32(hCPU->gpr[3], MEMORY_MAPABLE_PHYS_AREA_OFFSET);
|
||||
memory_writeU32(hCPU->gpr[4], MEMORY_MAPABLE_PHYS_AREA_SIZE);
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_OSAllocVirtAddr(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 MPTR address
|
||||
// r4 uint32 size
|
||||
// r5 uint32 align
|
||||
|
||||
uint32 address = hCPU->gpr[3];
|
||||
uint32 size = hCPU->gpr[4];
|
||||
uint32 align = hCPU->gpr[5];
|
||||
if (address != MPTR_NULL)
|
||||
{
|
||||
debug_printf("coreinitExport_OSAllocVirtAddr(): Unsupported address != NULL\n");
|
||||
debugBreakpoint();
|
||||
}
|
||||
if (align == 0)
|
||||
align = 1;
|
||||
if (align != 0 && align != 1)
|
||||
assert_dbg();
|
||||
|
||||
address = _VirtualMemoryAlloc(size, align);
|
||||
debug_printf("coreinitExport_OSAllocVirtAddr(): Allocated virtual memory at 0x%08x\n", address);
|
||||
osLib_returnFromFunction(hCPU, address);
|
||||
}
|
||||
|
||||
void coreinitExport_OSMapMemory(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 MPTR virtualAddress
|
||||
// r4 MPTR physicalAddress
|
||||
// r5 uint32 size
|
||||
// r6 uint32 mode
|
||||
MPTR virtualAddress = hCPU->gpr[3];
|
||||
MPTR physicalAddress = hCPU->gpr[4];
|
||||
uint32 size = hCPU->gpr[5];
|
||||
uint32 mode = hCPU->gpr[6];
|
||||
|
||||
if (virtualAddress < MEMORY_MAPABLE_VIRT_AREA_OFFSET || virtualAddress >= (MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE))
|
||||
cemu_assert_suspicious();
|
||||
|
||||
uint8* virtualPtr = memory_getPointerFromVirtualOffset(virtualAddress);
|
||||
|
||||
MemMapper::PAGE_PERMISSION pageProtect = MemMapper::PAGE_PERMISSION::P_NONE;
|
||||
if (mode == OS_MAP_READ_ONLY)
|
||||
pageProtect = MemMapper::PAGE_PERMISSION::P_READ;
|
||||
else if (mode == OS_MAP_READ_WRITE)
|
||||
pageProtect = MemMapper::PAGE_PERMISSION::P_RW;
|
||||
else
|
||||
cemu_assert_unimplemented();
|
||||
void* allocationResult = MemMapper::AllocateMemory(virtualPtr, size, pageProtect, true);
|
||||
if (!allocationResult)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "OSMapMemory failed");
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
}
|
||||
|
||||
void coreinitExport_OSUnmapMemory(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 MPTR virtualAddress
|
||||
// r4 uint32 size
|
||||
MPTR virtualAddress = hCPU->gpr[3];
|
||||
uint32 size = hCPU->gpr[4];
|
||||
|
||||
if (virtualAddress < MEMORY_MAPABLE_VIRT_AREA_OFFSET || virtualAddress >= (MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE))
|
||||
cemu_assert_suspicious();
|
||||
|
||||
cemu_assert((size % MemMapper::GetPageSize()) == 0);
|
||||
|
||||
uint8* virtualPtr = memory_getPointerFromVirtualOffset(virtualAddress);
|
||||
|
||||
MemMapper::FreeMemory(virtualPtr, size, true);
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
}
|
||||
|
||||
void InitializeMemoryMapping()
|
||||
{
|
||||
osLib_addFunction("coreinit", "OSGetAvailPhysAddrRange", coreinitExport_OSGetAvailPhysAddrRange);
|
||||
osLib_addFunction("coreinit", "OSAllocVirtAddr", coreinitExport_OSAllocVirtAddr);
|
||||
osLib_addFunction("coreinit", "OSMapMemory", coreinitExport_OSMapMemory);
|
||||
osLib_addFunction("coreinit", "OSUnmapMemory", coreinitExport_OSUnmapMemory);
|
||||
}
|
||||
}
|
||||
5
src/Cafe/OS/libs/coreinit/coreinit_MemoryMapping.h
Normal file
5
src/Cafe/OS/libs/coreinit/coreinit_MemoryMapping.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitializeMemoryMapping();
|
||||
}
|
||||
132
src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.cpp
Normal file
132
src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MessageQueue.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
SysAllocator<OSMessageQueue> g_systemMessageQueue;
|
||||
SysAllocator<OSMessage, 16> _systemMessageQueueArray;
|
||||
|
||||
void OSInitMessageQueueEx(OSMessageQueue* msgQueue, OSMessage* msgArray, uint32 msgCount, void* userData)
|
||||
{
|
||||
msgQueue->magic = 'mSgQ';
|
||||
msgQueue->userData = userData;
|
||||
msgQueue->msgArray = msgArray;
|
||||
msgQueue->msgCount = msgCount;
|
||||
msgQueue->firstIndex = 0;
|
||||
msgQueue->usedCount = 0;
|
||||
msgQueue->ukn08 = 0;
|
||||
OSInitThreadQueueEx(&msgQueue->threadQueueReceive, msgQueue);
|
||||
OSInitThreadQueueEx(&msgQueue->threadQueueSend, msgQueue);
|
||||
}
|
||||
|
||||
void OSInitMessageQueue(OSMessageQueue* msgQueue, OSMessage* msgArray, uint32 msgCount)
|
||||
{
|
||||
OSInitMessageQueueEx(msgQueue, msgArray, msgCount, nullptr);
|
||||
}
|
||||
|
||||
bool OSReceiveMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags)
|
||||
{
|
||||
__OSLockScheduler(msgQueue);
|
||||
while (msgQueue->usedCount == (uint32be)0)
|
||||
{
|
||||
if ((flags & OS_MESSAGE_BLOCK))
|
||||
{
|
||||
msgQueue->threadQueueReceive.queueAndWait(OSGetCurrentThread());
|
||||
}
|
||||
else
|
||||
{
|
||||
__OSUnlockScheduler(msgQueue);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// copy message
|
||||
sint32 messageIndex = msgQueue->firstIndex;
|
||||
OSMessage* readMsg = &(msgQueue->msgArray[messageIndex]);
|
||||
memcpy(msg, readMsg, sizeof(OSMessage));
|
||||
msgQueue->firstIndex = ((uint32)msgQueue->firstIndex + 1) % (uint32)(msgQueue->msgCount);
|
||||
msgQueue->usedCount = (uint32)msgQueue->usedCount - 1;
|
||||
// wake up any thread waiting to add a message
|
||||
if (!msgQueue->threadQueueSend.isEmpty())
|
||||
msgQueue->threadQueueSend.wakeupSingleThreadWaitQueue(true);
|
||||
__OSUnlockScheduler(msgQueue);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSPeekMessage(OSMessageQueue* msgQueue, OSMessage* msg)
|
||||
{
|
||||
__OSLockScheduler(msgQueue);
|
||||
if ((msgQueue->usedCount == (uint32be)0))
|
||||
{
|
||||
__OSUnlockScheduler(msgQueue);
|
||||
return false;
|
||||
}
|
||||
// copy message
|
||||
sint32 messageIndex = msgQueue->firstIndex;
|
||||
if (msg)
|
||||
{
|
||||
OSMessage* readMsg = &(msgQueue->msgArray[messageIndex]);
|
||||
memcpy(msg, readMsg, sizeof(OSMessage));
|
||||
}
|
||||
__OSUnlockScheduler(msgQueue);
|
||||
return true;
|
||||
}
|
||||
|
||||
sint32 OSSendMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
while (msgQueue->usedCount >= msgQueue->msgCount)
|
||||
{
|
||||
if ((flags & OS_MESSAGE_BLOCK))
|
||||
{
|
||||
msgQueue->threadQueueSend.queueAndWait(OSGetCurrentThread());
|
||||
}
|
||||
else
|
||||
{
|
||||
__OSUnlockScheduler();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// add message
|
||||
if ((flags & OS_MESSAGE_HIGH_PRIORITY))
|
||||
{
|
||||
// decrease firstIndex
|
||||
sint32 newFirstIndex = (sint32)((sint32)msgQueue->firstIndex + (sint32)msgQueue->msgCount - 1) % (sint32)msgQueue->msgCount;
|
||||
msgQueue->firstIndex = newFirstIndex;
|
||||
// insert message at new first index
|
||||
msgQueue->usedCount = (uint32)msgQueue->usedCount + 1;
|
||||
OSMessage* newMsg = &(msgQueue->msgArray[newFirstIndex]);
|
||||
memcpy(newMsg, msg, sizeof(OSMessage));
|
||||
}
|
||||
else
|
||||
{
|
||||
sint32 messageIndex = (uint32)(msgQueue->firstIndex + msgQueue->usedCount) % (uint32)msgQueue->msgCount;
|
||||
msgQueue->usedCount = (uint32)msgQueue->usedCount + 1;
|
||||
OSMessage* newMsg = &(msgQueue->msgArray[messageIndex]);
|
||||
memcpy(newMsg, msg, sizeof(OSMessage));
|
||||
}
|
||||
// wake up any thread waiting to read a message
|
||||
if (!msgQueue->threadQueueReceive.isEmpty())
|
||||
msgQueue->threadQueueReceive.wakeupSingleThreadWaitQueue(true);
|
||||
__OSUnlockScheduler();
|
||||
return 1;
|
||||
}
|
||||
|
||||
OSMessageQueue* OSGetSystemMessageQueue()
|
||||
{
|
||||
return g_systemMessageQueue.GetPtr();
|
||||
}
|
||||
|
||||
void InitializeMessageQueue()
|
||||
{
|
||||
OSInitMessageQueue(g_systemMessageQueue.GetPtr(), _systemMessageQueueArray.GetPtr(), _systemMessageQueueArray.GetCount());
|
||||
|
||||
cafeExportRegister("coreinit", OSInitMessageQueueEx, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSInitMessageQueue, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSReceiveMessage, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSPeekMessage, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSSendMessage, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSGetSystemMessageQueue, LogType::CoreinitThread);
|
||||
}
|
||||
};
|
||||
|
||||
40
src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.h
Normal file
40
src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct OSMessage
|
||||
{
|
||||
MPTR message;
|
||||
uint32 data0;
|
||||
uint32 data1;
|
||||
uint32 data2;
|
||||
};
|
||||
|
||||
struct OSMessageQueue
|
||||
{
|
||||
/* +0x00 */ uint32be magic;
|
||||
/* +0x04 */ MEMPTR<void> userData;
|
||||
/* +0x08 */ uint32be ukn08;
|
||||
/* +0x0C */ OSThreadQueue threadQueueSend;
|
||||
/* +0x1C */ OSThreadQueue threadQueueReceive;
|
||||
/* +0x2C */ MEMPTR<OSMessage> msgArray;
|
||||
/* +0x30 */ uint32be msgCount;
|
||||
/* +0x34 */ uint32be firstIndex;
|
||||
/* +0x38 */ uint32be usedCount;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSMessageQueue) == 0x3C);
|
||||
|
||||
// flags
|
||||
#define OS_MESSAGE_BLOCK 1 // blocking send/receive
|
||||
#define OS_MESSAGE_HIGH_PRIORITY 2 // put message in front of all queued messages
|
||||
|
||||
void OSInitMessageQueueEx(OSMessageQueue* msgQueue, OSMessage* msgArray, uint32 msgCount, void* userData);
|
||||
void OSInitMessageQueue(OSMessageQueue* msgQueue, OSMessage* msgArray, uint32 msgCount);
|
||||
bool OSReceiveMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags);
|
||||
bool OSPeekMessage(OSMessageQueue* msgQueue, OSMessage* msg);
|
||||
sint32 OSSendMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags);
|
||||
|
||||
void InitializeMessageQueue();
|
||||
};
|
||||
357
src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp
Normal file
357
src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
/* coreinit logging and string format */
|
||||
|
||||
sint32 ppcSprintf(const char* formatStr, char* strOut, sint32 maxLength, PPCInterpreter_t* hCPU, sint32 initialParamIndex)
|
||||
{
|
||||
char tempStr[4096];
|
||||
sint32 integerParamIndex = initialParamIndex;
|
||||
sint32 floatParamIndex = 0;
|
||||
sint32 writeIndex = 0;
|
||||
while (*formatStr)
|
||||
{
|
||||
char c = *formatStr;
|
||||
if (c == '%')
|
||||
{
|
||||
const char* formatStart = formatStr;
|
||||
formatStr++;
|
||||
if (*formatStr == '%')
|
||||
{
|
||||
// percent sign
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = '%';
|
||||
writeIndex++;
|
||||
formatStr++;
|
||||
continue;
|
||||
}
|
||||
// flags
|
||||
bool flag_leftJustify = false;
|
||||
bool flag_zeroPadding = false;
|
||||
if (*formatStr == '-')
|
||||
{
|
||||
flag_leftJustify = true;
|
||||
formatStr++;
|
||||
}
|
||||
if (*formatStr == '+')
|
||||
{
|
||||
// todo
|
||||
formatStr++;
|
||||
}
|
||||
if (*formatStr == ' ')
|
||||
{
|
||||
// todo
|
||||
formatStr++;
|
||||
}
|
||||
if (*formatStr == '#')
|
||||
{
|
||||
// todo
|
||||
formatStr++;
|
||||
}
|
||||
if (*formatStr == '0')
|
||||
{
|
||||
flag_zeroPadding = true;
|
||||
formatStr++;
|
||||
}
|
||||
// width
|
||||
if (*formatStr == '*')
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
formatStr++;
|
||||
}
|
||||
bool widthIsSpecified = false;
|
||||
sint32 width = 0;
|
||||
while (*formatStr >= '0' && *formatStr <= '9')
|
||||
{
|
||||
width *= 10;
|
||||
width += (*formatStr - '0');
|
||||
formatStr++;
|
||||
widthIsSpecified = true;
|
||||
}
|
||||
// precision
|
||||
if (*formatStr == '.')
|
||||
{
|
||||
formatStr++;
|
||||
if (*formatStr == '*')
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
while (*formatStr >= '0' && *formatStr <= '9')
|
||||
{
|
||||
formatStr++;
|
||||
}
|
||||
}
|
||||
// length + specifier
|
||||
char tempFormat[64];
|
||||
if (*formatStr == 'X' || *formatStr == 'x' || *formatStr == 'u' || *formatStr == 'd' || *formatStr == 'p' || *formatStr == 'i' ||
|
||||
(formatStr[0] == 'l' && formatStr[1] == 'd'))
|
||||
{
|
||||
// number
|
||||
formatStr++;
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU32(hCPU, integerParamIndex));
|
||||
integerParamIndex++;
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = tempStr[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
else if (*formatStr == 's')
|
||||
{
|
||||
// string
|
||||
formatStr++;
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
MPTR strOffset = PPCInterpreter_getCallParamU32(hCPU, integerParamIndex);
|
||||
sint32 tempLen = 0;
|
||||
if (strOffset == MPTR_NULL)
|
||||
tempLen = sprintf(tempStr, "NULL");
|
||||
else
|
||||
tempLen = sprintf(tempStr, tempFormat, memory_getPointerFromVirtualOffset(strOffset));
|
||||
integerParamIndex++;
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = tempStr[i];
|
||||
writeIndex++;
|
||||
}
|
||||
strOut[std::min(maxLength - 1, writeIndex)] = '\0';
|
||||
}
|
||||
else if (*formatStr == 'f')
|
||||
{
|
||||
// float
|
||||
formatStr++;
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (float)hCPU->fpr[1 + floatParamIndex].fp0);
|
||||
floatParamIndex++;
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = tempStr[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
else if (*formatStr == 'c')
|
||||
{
|
||||
// character
|
||||
formatStr++;
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU32(hCPU, integerParamIndex));
|
||||
integerParamIndex++;
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = tempStr[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
else if (formatStr[0] == 'l' && formatStr[1] == 'f')
|
||||
{
|
||||
// double
|
||||
formatStr += 2;
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (double)hCPU->fpr[1 + floatParamIndex].fp0);
|
||||
floatParamIndex++;
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = tempStr[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && (formatStr[2] == 'x' || formatStr[2] == 'X')))
|
||||
{
|
||||
formatStr += 3;
|
||||
// number (64bit)
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
if (integerParamIndex & 1)
|
||||
integerParamIndex++;
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU64(hCPU, integerParamIndex));
|
||||
integerParamIndex += 2;
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = tempStr[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// unsupported / unknown specifier
|
||||
cemu_assert_debug(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = c;
|
||||
writeIndex++;
|
||||
formatStr++;
|
||||
}
|
||||
}
|
||||
strOut[std::min(writeIndex, maxLength - 1)] = '\0';
|
||||
return std::min(writeIndex, maxLength - 1);
|
||||
}
|
||||
|
||||
sint32 __os_snprintf(char* outputStr, sint32 maxLength, const char* formatStr)
|
||||
{
|
||||
sint32 r = ppcSprintf(formatStr, outputStr, maxLength, ppcInterpreterCurrentInstance, 3);
|
||||
return r;
|
||||
}
|
||||
|
||||
enum class CafeLogType
|
||||
{
|
||||
OSCONSOLE = 0,
|
||||
};
|
||||
|
||||
struct CafeLogBuffer
|
||||
{
|
||||
std::array<char, 270> lineBuffer;
|
||||
size_t lineLength{};
|
||||
};
|
||||
|
||||
CafeLogBuffer g_logBuffer_OSReport;
|
||||
|
||||
CafeLogBuffer& getLogBuffer(CafeLogType cafeLogType)
|
||||
{
|
||||
if (cafeLogType == CafeLogType::OSCONSOLE)
|
||||
return g_logBuffer_OSReport;
|
||||
// default to OSReport
|
||||
return g_logBuffer_OSReport;
|
||||
}
|
||||
|
||||
std::string_view getLogBufferName(CafeLogType cafeLogType)
|
||||
{
|
||||
if (cafeLogType == CafeLogType::OSCONSOLE)
|
||||
return "OSConsole";
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
void WriteCafeConsole(CafeLogType cafeLogType, const char* msg, sint32 len)
|
||||
{
|
||||
// once a line is full or \n is written it will be posted to log
|
||||
CafeLogBuffer& logBuffer = getLogBuffer(cafeLogType);
|
||||
|
||||
auto flushLine = [](CafeLogBuffer& cafeLogBuffer, std::string_view cafeLogName) -> void
|
||||
{
|
||||
cemuLog_log(LogType::CoreinitLogging, "[{0}] {1}", cafeLogName, std::basic_string_view(cafeLogBuffer.lineBuffer.data(), cafeLogBuffer.lineLength));
|
||||
cafeLogBuffer.lineLength = 0;
|
||||
};
|
||||
|
||||
while (len)
|
||||
{
|
||||
char c = *msg;
|
||||
msg++;
|
||||
len--;
|
||||
if (c == '\r')
|
||||
continue;
|
||||
if (c == '\n')
|
||||
{
|
||||
// flush line immediately
|
||||
flushLine(logBuffer, getLogBufferName(cafeLogType));
|
||||
continue;
|
||||
}
|
||||
logBuffer.lineBuffer[logBuffer.lineLength] = c;
|
||||
logBuffer.lineLength++;
|
||||
if (logBuffer.lineLength >= logBuffer.lineBuffer.size())
|
||||
flushLine(logBuffer, getLogBufferName(cafeLogType));
|
||||
}
|
||||
}
|
||||
|
||||
void OSReport(const char* format)
|
||||
{
|
||||
char buffer[1024 * 2];
|
||||
sint32 len = ppcSprintf(format, buffer, sizeof(buffer), ppcInterpreterCurrentInstance, 1);
|
||||
WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len);
|
||||
}
|
||||
|
||||
void OSVReport(const char* format, MPTR vaArgs)
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
void COSWarn()
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
|
||||
void OSLogPrintf()
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
|
||||
void OSConsoleWrite(const char* strPtr, sint32 length)
|
||||
{
|
||||
if (length < 0)
|
||||
return;
|
||||
WriteCafeConsole(CafeLogType::OSCONSOLE, strPtr, length);
|
||||
}
|
||||
|
||||
/* home button menu */
|
||||
|
||||
bool g_homeButtonMenuEnabled = false;
|
||||
|
||||
bool OSIsHomeButtonMenuEnabled()
|
||||
{
|
||||
return g_homeButtonMenuEnabled;
|
||||
}
|
||||
|
||||
bool OSEnableHomeButtonMenu(bool enable)
|
||||
{
|
||||
g_homeButtonMenuEnabled = enable;
|
||||
return true;
|
||||
}
|
||||
|
||||
void miscInit()
|
||||
{
|
||||
cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSReport, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSVReport, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSConsoleWrite, LogType::Placeholder);
|
||||
|
||||
g_homeButtonMenuEnabled = true; // enabled by default
|
||||
// Disney Infinity 2.0 actually relies on home button menu being enabled by default. If it's false it will crash due to calling erreula->IsAppearHomeNixSign() before initializing erreula
|
||||
cafeExportRegister("coreinit", OSIsHomeButtonMenuEnabled, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSEnableHomeButtonMenu, LogType::CoreinitThread);
|
||||
}
|
||||
|
||||
};
|
||||
6
src/Cafe/OS/libs/coreinit/coreinit_Misc.h
Normal file
6
src/Cafe/OS/libs/coreinit/coreinit_Misc.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void miscInit();
|
||||
};
|
||||
193
src/Cafe/OS/libs/coreinit/coreinit_OSScreen.cpp
Normal file
193
src/Cafe/OS/libs/coreinit/coreinit_OSScreen.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/gx2/GX2.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_OSScreen_font.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_OSScreen.h"
|
||||
|
||||
#define OSSCREEN_TV (0)
|
||||
#define OSSCREEN_DRC (1)
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
struct
|
||||
{
|
||||
sint32 x;
|
||||
sint32 y;
|
||||
sint32 pitch;
|
||||
}screenSizes[2] =
|
||||
{
|
||||
{ 1280, 720, 1280}, // TV
|
||||
{ 896, 480, 896 } // DRC (values might be incorrect)
|
||||
};
|
||||
|
||||
void* currentScreenBasePtr[2] = { 0 };
|
||||
|
||||
void _OSScreen_Clear(uint32 screenIndex, uint32 color)
|
||||
{
|
||||
if (!currentScreenBasePtr[screenIndex])
|
||||
return;
|
||||
|
||||
uint32* output = (uint32*)currentScreenBasePtr[screenIndex];
|
||||
sint32 sizeInPixels = screenSizes[screenIndex].pitch * screenSizes[screenIndex].y;
|
||||
color = _swapEndianU32(color);
|
||||
for (sint32 i = 0; i < sizeInPixels; i++)
|
||||
{
|
||||
*output = color;
|
||||
output++;
|
||||
}
|
||||
}
|
||||
|
||||
void coreinitExport_OSScreenInit(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// todo - init VI registers?
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_OSScreenGetBufferSizeEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(screenIndex, 0);
|
||||
cemu_assert(screenIndex < 2);
|
||||
uint32 bufferSize = screenSizes[screenIndex].pitch * screenSizes[screenIndex].y * 4 * 2;
|
||||
osLib_returnFromFunction(hCPU, bufferSize);
|
||||
}
|
||||
|
||||
void _updateCurrentDrawScreen(sint32 screenIndex)
|
||||
{
|
||||
uint32 screenDataSize = screenSizes[screenIndex].pitch * screenSizes[screenIndex].y * 4;
|
||||
|
||||
if ((LatteGPUState.osScreen.screen[screenIndex].flipRequestCount & 1) != 0)
|
||||
currentScreenBasePtr[screenIndex] = memory_getPointerFromPhysicalOffset(LatteGPUState.osScreen.screen[screenIndex].physPtr + screenDataSize);
|
||||
else
|
||||
currentScreenBasePtr[screenIndex] = memory_getPointerFromPhysicalOffset(LatteGPUState.osScreen.screen[screenIndex].physPtr);
|
||||
}
|
||||
|
||||
void coreinitExport_OSScreenSetBufferEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(screenIndex, 0);
|
||||
ppcDefineParamU32(buffer, 1);
|
||||
cemu_assert(screenIndex < 2);
|
||||
LatteGPUState.osScreen.screen[screenIndex].physPtr = buffer;
|
||||
_updateCurrentDrawScreen(screenIndex);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_OSScreenEnableEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(screenIndex, 0);
|
||||
ppcDefineParamU32(isEnabled, 1);
|
||||
cemu_assert(screenIndex < 2);
|
||||
LatteGPUState.osScreen.screen[screenIndex].isEnabled = isEnabled != 0;
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_OSScreenClearBufferEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(screenIndex, 0);
|
||||
ppcDefineParamU32(color, 1);
|
||||
cemu_assert(screenIndex < 2);
|
||||
_OSScreen_Clear(screenIndex, color);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_OSScreenFlipBuffersEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(screenIndex, 0);
|
||||
cemu_assert(screenIndex < 2);
|
||||
forceLogDebug_printf("OSScreenFlipBuffersEx %d", screenIndex);
|
||||
LatteGPUState.osScreen.screen[screenIndex].flipRequestCount++;
|
||||
_updateCurrentDrawScreen(screenIndex);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_OSScreenPutPixelEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(screenIndex, 0);
|
||||
ppcDefineParamS32(x, 1);
|
||||
ppcDefineParamS32(y, 2);
|
||||
ppcDefineParamU32(color, 3);
|
||||
if (screenIndex >= 2)
|
||||
{
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
if (x >= 0 && x < screenSizes[screenIndex].x && y >= 0 && y < screenSizes[screenIndex].y)
|
||||
{
|
||||
*(uint32*)((uint8*)currentScreenBasePtr[screenIndex] + (x + y * screenSizes[screenIndex].pitch) * 4) = _swapEndianS32(color);
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
const char* osScreenCharset = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
|
||||
|
||||
sint32 _getOSScreenFontCharIndex(char c)
|
||||
{
|
||||
const char* charset = osScreenCharset;
|
||||
while (*charset)
|
||||
{
|
||||
if (*charset == c)
|
||||
{
|
||||
return (sint32)(charset - osScreenCharset);
|
||||
}
|
||||
charset++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void coreinitExport_OSScreenPutFontEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(screenIndex, 0);
|
||||
ppcDefineParamS32(x, 1);
|
||||
ppcDefineParamS32(y, 2);
|
||||
ppcDefineParamStr(str, 3);
|
||||
|
||||
// characters are:
|
||||
// 16 x 32 (including the margin)
|
||||
// with a margin of 4 x 8
|
||||
|
||||
if (y < 0)
|
||||
{
|
||||
debug_printf("OSScreenPutFontEx: y has invalid value\n");
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
sint32 px = x * 16;
|
||||
sint32 py = y * 24;
|
||||
|
||||
while (*str)
|
||||
{
|
||||
sint32 charIndex = _getOSScreenFontCharIndex(*str);
|
||||
if (charIndex >= 0)
|
||||
{
|
||||
const uint8* charBitmap = osscreenBitmapFont + charIndex * 50;
|
||||
for (sint32 fy = 0; fy < 25; fy++)
|
||||
{
|
||||
for (sint32 fx = 0; fx < 14; fx++)
|
||||
{
|
||||
if (((charBitmap[(fx / 8) + (fy) * 2] >> (7 - (fx & 7))) & 1) == 0)
|
||||
continue;
|
||||
*(uint32*)((uint8*)currentScreenBasePtr[screenIndex] + ((px + fx) + (py + fy) * screenSizes[screenIndex].pitch) * 4) = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
px += 16;
|
||||
str++;
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void InitializeOSScreen()
|
||||
{
|
||||
osLib_addFunction("coreinit", "OSScreenInit", coreinitExport_OSScreenInit);
|
||||
osLib_addFunction("coreinit", "OSScreenGetBufferSizeEx", coreinitExport_OSScreenGetBufferSizeEx);
|
||||
osLib_addFunction("coreinit", "OSScreenSetBufferEx", coreinitExport_OSScreenSetBufferEx);
|
||||
osLib_addFunction("coreinit", "OSScreenEnableEx", coreinitExport_OSScreenEnableEx);
|
||||
osLib_addFunction("coreinit", "OSScreenClearBufferEx", coreinitExport_OSScreenClearBufferEx);
|
||||
osLib_addFunction("coreinit", "OSScreenFlipBuffersEx", coreinitExport_OSScreenFlipBuffersEx);
|
||||
osLib_addFunction("coreinit", "OSScreenPutPixelEx", coreinitExport_OSScreenPutPixelEx);
|
||||
osLib_addFunction("coreinit", "OSScreenPutFontEx", coreinitExport_OSScreenPutFontEx);
|
||||
}
|
||||
}
|
||||
6
src/Cafe/OS/libs/coreinit/coreinit_OSScreen.h
Normal file
6
src/Cafe/OS/libs/coreinit/coreinit_OSScreen.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitializeOSScreen();
|
||||
}
|
||||
2542
src/Cafe/OS/libs/coreinit/coreinit_OSScreen_font.h
Normal file
2542
src/Cafe/OS/libs/coreinit/coreinit_OSScreen_font.h
Normal file
File diff suppressed because it is too large
Load diff
34
src/Cafe/OS/libs/coreinit/coreinit_OverlayArena.cpp
Normal file
34
src/Cafe/OS/libs/coreinit/coreinit_OverlayArena.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "coreinit_OverlayArena.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool isEnabled;
|
||||
}g_coreinitOverlayArena = { 0 };
|
||||
|
||||
uint32 OSIsEnabledOverlayArena()
|
||||
{
|
||||
return g_coreinitOverlayArena.isEnabled ? 1 : 0;
|
||||
}
|
||||
|
||||
void OSEnableOverlayArena(uint32 uknParam, uint32be* areaOffset, uint32be* areaSize)
|
||||
{
|
||||
if (g_coreinitOverlayArena.isEnabled == false)
|
||||
{
|
||||
memory_enableOverlayArena();
|
||||
g_coreinitOverlayArena.isEnabled = true;
|
||||
}
|
||||
*areaOffset = MEMORY_OVERLAY_AREA_OFFSET;
|
||||
*areaSize = MEMORY_OVERLAY_AREA_SIZE;
|
||||
}
|
||||
|
||||
void InitializeOverlayArena()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSIsEnabledOverlayArena, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSEnableOverlayArena, LogType::Placeholder);
|
||||
g_coreinitOverlayArena.isEnabled = false;
|
||||
}
|
||||
}
|
||||
4
src/Cafe/OS/libs/coreinit/coreinit_OverlayArena.h
Normal file
4
src/Cafe/OS/libs/coreinit/coreinit_OverlayArena.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
namespace coreinit
|
||||
{
|
||||
void InitializeOverlayArena();
|
||||
};
|
||||
128
src/Cafe/OS/libs/coreinit/coreinit_Scheduler.cpp
Normal file
128
src/Cafe/OS/libs/coreinit/coreinit_Scheduler.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "coreinit_Scheduler.h"
|
||||
|
||||
thread_local sint32 s_schedulerLockCount = 0;
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
#include <synchapi.h>
|
||||
CRITICAL_SECTION s_csSchedulerLock;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
pthread_mutex_t s_ptmSchedulerLock;
|
||||
#endif
|
||||
|
||||
void __OSLockScheduler(void* obj)
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
EnterCriticalSection(&s_csSchedulerLock);
|
||||
#else
|
||||
pthread_mutex_lock(&s_ptmSchedulerLock);
|
||||
#endif
|
||||
s_schedulerLockCount++;
|
||||
cemu_assert_debug(s_schedulerLockCount <= 1); // >= 2 should not happen. Scheduler lock does not allow recursion
|
||||
}
|
||||
|
||||
bool __OSHasSchedulerLock()
|
||||
{
|
||||
return s_schedulerLockCount > 0;
|
||||
}
|
||||
|
||||
bool __OSTryLockScheduler(void* obj)
|
||||
{
|
||||
bool r;
|
||||
#if BOOST_OS_WINDOWS
|
||||
r = TryEnterCriticalSection(&s_csSchedulerLock);
|
||||
#else
|
||||
r = pthread_mutex_trylock(&s_ptmSchedulerLock) == 0;
|
||||
#endif
|
||||
if (r)
|
||||
{
|
||||
s_schedulerLockCount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void __OSUnlockScheduler(void* obj)
|
||||
{
|
||||
s_schedulerLockCount--;
|
||||
cemu_assert_debug(s_schedulerLockCount >= 0);
|
||||
#if BOOST_OS_WINDOWS
|
||||
LeaveCriticalSection(&s_csSchedulerLock);
|
||||
#else
|
||||
pthread_mutex_unlock(&s_ptmSchedulerLock);
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
uint32 OSIsInterruptEnabled()
|
||||
{
|
||||
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
|
||||
if (hCPU == nullptr)
|
||||
return 0;
|
||||
|
||||
return hCPU->coreInterruptMask;
|
||||
}
|
||||
|
||||
// disables interrupts and scheduling
|
||||
uint32 OSDisableInterrupts()
|
||||
{
|
||||
// todo - rename SchedulerLock.cpp/h to Scheduler.cpp and move this there?
|
||||
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
|
||||
if (hCPU == nullptr)
|
||||
return 0;
|
||||
uint32 prevInterruptMask = hCPU->coreInterruptMask;
|
||||
if (hCPU->coreInterruptMask != 0)
|
||||
{
|
||||
// we have no efficient method to turn off scheduling completely, so instead we just increase the remaining cycles
|
||||
if (hCPU->remainingCycles >= 0x40000000)
|
||||
{
|
||||
forceLogDebug_printf("OSDisableInterrupts(): Warning - Interrupts already disabled? remCycles %08x LR %08x", hCPU->remainingCycles, hCPU->spr.LR);
|
||||
}
|
||||
hCPU->remainingCycles += 0x40000000;
|
||||
}
|
||||
hCPU->coreInterruptMask = 0;
|
||||
return prevInterruptMask;
|
||||
}
|
||||
|
||||
uint32 OSRestoreInterrupts(uint32 interruptMask)
|
||||
{
|
||||
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
|
||||
if (hCPU == nullptr)
|
||||
return 0;
|
||||
uint32 prevInterruptMask = hCPU->coreInterruptMask;
|
||||
if (hCPU->coreInterruptMask == 0 && interruptMask != 0)
|
||||
{
|
||||
hCPU->remainingCycles -= 0x40000000;
|
||||
}
|
||||
hCPU->coreInterruptMask = interruptMask;
|
||||
return prevInterruptMask;
|
||||
}
|
||||
|
||||
uint32 OSEnableInterrupts()
|
||||
{
|
||||
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
|
||||
uint32 prevInterruptMask = hCPU->coreInterruptMask;
|
||||
OSRestoreInterrupts(1);
|
||||
return prevInterruptMask;
|
||||
}
|
||||
|
||||
void InitializeSchedulerLock()
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
InitializeCriticalSection(&s_csSchedulerLock);
|
||||
#else
|
||||
pthread_mutexattr_t ma;
|
||||
pthread_mutexattr_init(&ma);
|
||||
pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&s_ptmSchedulerLock, &ma);
|
||||
#endif
|
||||
cafeExportRegister("coreinit", __OSLockScheduler, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", __OSUnlockScheduler, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", OSDisableInterrupts, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSEnableInterrupts, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSRestoreInterrupts, LogType::CoreinitThread);
|
||||
}
|
||||
};
|
||||
16
src/Cafe/OS/libs/coreinit/coreinit_Scheduler.h
Normal file
16
src/Cafe/OS/libs/coreinit/coreinit_Scheduler.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
void __OSLockScheduler(void* obj = nullptr);
|
||||
bool __OSHasSchedulerLock();
|
||||
bool __OSTryLockScheduler(void* obj = nullptr);
|
||||
void __OSUnlockScheduler(void* obj = nullptr);
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
uint32 OSIsInterruptEnabled();
|
||||
uint32 OSDisableInterrupts();
|
||||
uint32 OSRestoreInterrupts(uint32 interruptMask);
|
||||
uint32 OSEnableInterrupts();
|
||||
|
||||
void InitializeSchedulerLock();
|
||||
}
|
||||
217
src/Cafe/OS/libs/coreinit/coreinit_Spinlock.cpp
Normal file
217
src/Cafe/OS/libs/coreinit/coreinit_Spinlock.cpp
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void __OSBoostThread(OSThread_t* thread)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
thread->stateFlags |= 0x20000;
|
||||
thread->context.boostCount += 1;
|
||||
__OSUpdateThreadEffectivePriority(thread); // sets thread->effectivePriority to zero since boostCount != 0
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void __OSDeboostThread(OSThread_t* thread)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
cemu_assert_debug(thread->context.boostCount != 0);
|
||||
thread->context.boostCount -= 1;
|
||||
if (thread->context.boostCount == 0)
|
||||
{
|
||||
thread->stateFlags &= ~0x20000;
|
||||
__OSUpdateThreadEffectivePriority(thread);
|
||||
// todo - reschedule if lower priority than other threads on current core?
|
||||
}
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void OSInitSpinLock(OSSpinLock* spinlock)
|
||||
{
|
||||
spinlock->userData = spinlock;
|
||||
spinlock->ownerThread = nullptr;
|
||||
spinlock->count = 0;
|
||||
spinlock->interruptMask = 1;
|
||||
}
|
||||
|
||||
bool OSAcquireSpinLock(OSSpinLock* spinlock)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
if (spinlock->ownerThread == currentThread)
|
||||
{
|
||||
spinlock->count += 1;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// loop until lock acquired
|
||||
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
|
||||
{
|
||||
OSYieldThread();
|
||||
}
|
||||
}
|
||||
__OSBoostThread(currentThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSTryAcquireSpinLock(OSSpinLock* spinlock)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
if (spinlock->ownerThread == currentThread)
|
||||
{
|
||||
spinlock->count += 1;
|
||||
return true;
|
||||
}
|
||||
// try acquire once
|
||||
if (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
|
||||
return false;
|
||||
__OSBoostThread(currentThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSTryAcquireSpinLockWithTimeout(OSSpinLock* spinlock, uint64 timeout)
|
||||
{
|
||||
// used by CoD: Ghosts
|
||||
cemu_assert_debug((timeout >> 63) == 0); // negative?
|
||||
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
if (spinlock->ownerThread == currentThread)
|
||||
{
|
||||
spinlock->count += 1;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// loop until lock acquired or timeout occurred
|
||||
uint64 timeoutValue = coreinit_getTimerTick() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout);
|
||||
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
|
||||
{
|
||||
OSYieldThread();
|
||||
if (coreinit_getTimerTick() >= timeoutValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
__OSBoostThread(currentThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSReleaseSpinLock(OSSpinLock* spinlock)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
cemu_assert_debug(spinlock->ownerThread == currentThread);
|
||||
if (spinlock->count != 0)
|
||||
{
|
||||
spinlock->count -= 1;
|
||||
return true;
|
||||
}
|
||||
// release spinlock
|
||||
while (!spinlock->ownerThread.atomic_compare_exchange(currentThread, nullptr));
|
||||
__OSDeboostThread(currentThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSUninterruptibleSpinLock_Acquire(OSSpinLock* spinlock)
|
||||
{
|
||||
// frequently used by VC DS
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
cemu_assert_debug(currentThread != nullptr);
|
||||
if (spinlock->ownerThread == currentThread)
|
||||
{
|
||||
spinlock->count += 1;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// loop until lock acquired
|
||||
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
|
||||
{
|
||||
OSYieldThread();
|
||||
}
|
||||
}
|
||||
__OSBoostThread(currentThread);
|
||||
spinlock->interruptMask = OSDisableInterrupts();
|
||||
cemu_assert_debug(spinlock->ownerThread == currentThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSUninterruptibleSpinLock_TryAcquire(OSSpinLock* spinlock)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
if (spinlock->ownerThread == currentThread)
|
||||
{
|
||||
spinlock->count += 1;
|
||||
return true;
|
||||
}
|
||||
// try acquire once
|
||||
if (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
|
||||
return false;
|
||||
__OSBoostThread(currentThread);
|
||||
spinlock->interruptMask = OSDisableInterrupts();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSUninterruptibleSpinLock_TryAcquireWithTimeout(OSSpinLock* spinlock, uint64 timeout)
|
||||
{
|
||||
cemu_assert_debug((timeout >> 63) == 0); // negative?
|
||||
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
if (spinlock->ownerThread == currentThread)
|
||||
{
|
||||
spinlock->count += 1;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// loop until lock acquired or timeout occurred
|
||||
uint64 timeoutValue = coreinit_getTimerTick() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout);
|
||||
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
|
||||
{
|
||||
OSYieldThread();
|
||||
if (coreinit_getTimerTick() >= timeoutValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
__OSBoostThread(currentThread);
|
||||
spinlock->interruptMask = OSDisableInterrupts();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSUninterruptibleSpinLock_Release(OSSpinLock* spinlock)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
cemu_assert_debug(spinlock->ownerThread == currentThread);
|
||||
if (spinlock->count != 0)
|
||||
{
|
||||
spinlock->count -= 1;
|
||||
return true;
|
||||
}
|
||||
// release spinlock
|
||||
OSRestoreInterrupts(spinlock->interruptMask);
|
||||
spinlock->interruptMask = 1;
|
||||
while (!spinlock->ownerThread.atomic_compare_exchange(currentThread, nullptr));
|
||||
__OSDeboostThread(currentThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitializeSpinlock()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSInitSpinLock, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSAcquireSpinLock, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSTryAcquireSpinLock, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSTryAcquireSpinLockWithTimeout, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSReleaseSpinLock, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", OSUninterruptibleSpinLock_Acquire, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSUninterruptibleSpinLock_TryAcquire, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSUninterruptibleSpinLock_TryAcquireWithTimeout, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSUninterruptibleSpinLock_Release, LogType::Placeholder);
|
||||
}
|
||||
#pragma endregion
|
||||
}
|
||||
28
src/Cafe/OS/libs/coreinit/coreinit_Spinlock.h
Normal file
28
src/Cafe/OS/libs/coreinit/coreinit_Spinlock.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct OSSpinLock
|
||||
{
|
||||
/* +0x00 */ MEMPTR<struct OSThread_t> ownerThread;
|
||||
/* +0x04 */ MEMPTR<void> userData;
|
||||
/* +0x08 */ uint32be count;
|
||||
/* +0x0C */ uint32be interruptMask;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSSpinLock) == 0x10);
|
||||
|
||||
void InitializeSpinlock();
|
||||
|
||||
void OSInitSpinLock(OSSpinLock* spinlock);
|
||||
|
||||
bool OSAcquireSpinLock(OSSpinLock* spinlock);
|
||||
bool OSTryAcquireSpinLock(OSSpinLock* spinlock);
|
||||
bool OSTryAcquireSpinLockWithTimeout(OSSpinLock* spinlock, uint64 timeout);
|
||||
bool OSReleaseSpinLock(OSSpinLock* spinlock);
|
||||
|
||||
bool OSUninterruptibleSpinLock_Acquire(OSSpinLock* spinlock);
|
||||
bool OSUninterruptibleSpinLock_TryAcquire(OSSpinLock* spinlock);
|
||||
bool OSUninterruptibleSpinLock_TryAcquireWithTimeout(OSSpinLock* spinlock, uint64 timeout);
|
||||
bool OSUninterruptibleSpinLock_Release(OSSpinLock* spinlock);
|
||||
}
|
||||
669
src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp
Normal file
669
src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp
Normal file
|
|
@ -0,0 +1,669 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "util/helpers/fspinlock.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
/************* OSEvent ************/
|
||||
|
||||
void OSInitEvent(OSEvent* event, OSEvent::EVENT_STATE initialState, OSEvent::EVENT_MODE mode)
|
||||
{
|
||||
event->magic = 'eVnT';
|
||||
cemu_assert_debug(event->magic == 0x65566e54);
|
||||
event->userData = nullptr;
|
||||
event->ukn08 = 0;
|
||||
event->state = initialState;
|
||||
event->mode = mode;
|
||||
OSInitThreadQueueEx(&event->threadQueue, event);
|
||||
}
|
||||
|
||||
void OSInitEventEx(OSEvent* event, OSEvent::EVENT_STATE initialState, OSEvent::EVENT_MODE mode, void* userData)
|
||||
{
|
||||
OSInitEvent(event, initialState, mode);
|
||||
event->userData = userData;
|
||||
}
|
||||
|
||||
void OSResetEvent(OSEvent* event)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
|
||||
event->state = OSEvent::EVENT_STATE::STATE_NOT_SIGNALED;
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void OSWaitEventInternal(OSEvent* event)
|
||||
{
|
||||
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
|
||||
{
|
||||
if (event->mode == OSEvent::EVENT_MODE::MODE_AUTO)
|
||||
event->state = OSEvent::EVENT_STATE::STATE_NOT_SIGNALED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// enter wait queue
|
||||
event->threadQueue.queueAndWait(OSGetCurrentThread());
|
||||
}
|
||||
}
|
||||
|
||||
void OSWaitEvent(OSEvent* event)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
OSWaitEventInternal(event);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
struct WaitEventWithTimeoutData
|
||||
{
|
||||
OSThread_t* thread;
|
||||
OSThreadQueue* threadQueue;
|
||||
std::atomic_bool hasTimeout;
|
||||
};
|
||||
|
||||
void _OSWaitEventWithTimeoutHandler(uint64 currentTick, void* context)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
WaitEventWithTimeoutData* data = (WaitEventWithTimeoutData*)context;
|
||||
if (data->thread->state == OSThread_t::THREAD_STATE::STATE_WAITING)
|
||||
{
|
||||
data->hasTimeout = true;
|
||||
data->threadQueue->cancelWait(data->thread);
|
||||
}
|
||||
}
|
||||
|
||||
uint64 coreinit_getOSTime();
|
||||
|
||||
bool OSWaitEventWithTimeout(OSEvent* event, uint64 timeout)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
|
||||
{
|
||||
if (event->mode == OSEvent::EVENT_MODE::MODE_AUTO)
|
||||
event->state = OSEvent::EVENT_STATE::STATE_NOT_SIGNALED;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (timeout == 0)
|
||||
{
|
||||
// fail immediately
|
||||
__OSUnlockScheduler();
|
||||
return false;
|
||||
}
|
||||
// wait and set timeout
|
||||
|
||||
// workaround for a bad implementation in some Unity games (like Qube Directors Cut, see FEventWiiU::Wait)
|
||||
// where the the return value of OSWaitEventWithTimeout is ignored and instead the game measures the elapsed time to determine if a timeout occurred
|
||||
timeout = timeout * 98ULL / 100ULL; // 98% (we want the function to return slightly before the actual timeout)
|
||||
|
||||
WaitEventWithTimeoutData data;
|
||||
data.thread = OSGetCurrentThread();
|
||||
data.threadQueue = &event->threadQueue;
|
||||
data.hasTimeout = false;
|
||||
|
||||
auto hostAlarm = coreinit::OSHostAlarmCreate(coreinit::coreinit_getOSTime() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout), 0, _OSWaitEventWithTimeoutHandler, &data);
|
||||
event->threadQueue.queueAndWait(OSGetCurrentThread());
|
||||
coreinit::OSHostAlarmDestroy(hostAlarm);
|
||||
if (data.hasTimeout)
|
||||
{
|
||||
__OSUnlockScheduler();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
__OSUnlockScheduler();
|
||||
return true;
|
||||
}
|
||||
|
||||
void OSSignalEventInternal(OSEvent* event)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (event->mode == OSEvent::EVENT_MODE::MODE_AUTO)
|
||||
{
|
||||
// in auto mode wake up one thread or if there is none then set signaled
|
||||
if (event->threadQueue.isEmpty())
|
||||
event->state = OSEvent::EVENT_STATE::STATE_SIGNALED;
|
||||
else
|
||||
event->threadQueue.wakeupSingleThreadWaitQueue(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in manual mode wake up all threads and set to signaled
|
||||
event->state = OSEvent::EVENT_STATE::STATE_SIGNALED;
|
||||
event->threadQueue.wakeupEntireWaitQueue(true);
|
||||
}
|
||||
}
|
||||
|
||||
void OSSignalEvent(OSEvent* event)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
OSSignalEventInternal(event);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void OSSignalEventAllInternal(OSEvent* event)
|
||||
{
|
||||
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (event->mode == OSEvent::EVENT_MODE::MODE_AUTO)
|
||||
{
|
||||
// in auto mode wake up one thread or if there is none then set signaled
|
||||
if (event->threadQueue.isEmpty())
|
||||
event->state = OSEvent::EVENT_STATE::STATE_SIGNALED;
|
||||
else
|
||||
event->threadQueue.wakeupEntireWaitQueue(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in manual mode wake up all threads and set to signaled
|
||||
event->state = OSEvent::EVENT_STATE::STATE_SIGNALED;
|
||||
event->threadQueue.wakeupEntireWaitQueue(true);
|
||||
}
|
||||
}
|
||||
|
||||
void OSSignalEventAll(OSEvent* event)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
OSSignalEventAllInternal(event);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
/************* OSRendezvous ************/
|
||||
|
||||
SysAllocator<OSEvent> g_rendezvousEvent;
|
||||
|
||||
void OSInitRendezvous(OSRendezvous* rendezvous)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
rendezvous->userData = rendezvous;
|
||||
for (sint32 i = 0; i < PPC_CORE_COUNT; i++)
|
||||
rendezvous->coreHit[i] = 0;
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
bool OSWaitRendezvous(OSRendezvous* rendezvous, uint32 coreMask)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
rendezvous->coreHit[OSGetCoreId()] = 1;
|
||||
OSSignalEventAllInternal(g_rendezvousEvent.GetPtr());
|
||||
while (true)
|
||||
{
|
||||
bool metAll = true;
|
||||
for(sint32 i=0; i<PPC_CORE_COUNT; i++)
|
||||
{
|
||||
if( (coreMask & (1<<i)) == 0 )
|
||||
continue; // core not required by core mask
|
||||
if (rendezvous->coreHit[i] == 0)
|
||||
{
|
||||
metAll = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (metAll)
|
||||
break;
|
||||
OSWaitEventInternal(g_rendezvousEvent.GetPtr());
|
||||
}
|
||||
__OSUnlockScheduler();
|
||||
return true;
|
||||
}
|
||||
|
||||
/************* OSMutex ************/
|
||||
|
||||
void OSInitMutexEx(OSMutex* mutex, void* userData)
|
||||
{
|
||||
mutex->magic = 'mUtX';
|
||||
mutex->userData = userData;
|
||||
mutex->ukn08 = 0;
|
||||
mutex->owner = nullptr;
|
||||
mutex->lockCount = 0;
|
||||
OSInitThreadQueueEx(&mutex->threadQueue, mutex);
|
||||
}
|
||||
|
||||
void OSInitMutex(OSMutex* mutex)
|
||||
{
|
||||
OSInitMutexEx(mutex, nullptr);
|
||||
}
|
||||
|
||||
void OSLockMutexInternal(OSMutex* mutex)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
int_fast32_t failedAttempts = 0;
|
||||
while (true)
|
||||
{
|
||||
if (mutex->owner == nullptr)
|
||||
{
|
||||
// acquire lock
|
||||
mutex->owner = currentThread;
|
||||
cemu_assert_debug(mutex->lockCount == 0);
|
||||
mutex->lockCount = 1;
|
||||
// cemu_assert_debug(mutex->next == nullptr && mutex->prev == nullptr); -> not zero initialized
|
||||
currentThread->mutexQueue.addMutex(mutex);
|
||||
break;
|
||||
}
|
||||
else if (mutex->owner == currentThread)
|
||||
{
|
||||
mutex->lockCount = mutex->lockCount + 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (failedAttempts >= 0x800)
|
||||
cemuLog_force("Detected long-term contested OSLockMutex");
|
||||
currentThread->waitingForMutex = mutex;
|
||||
mutex->threadQueue.queueAndWait(currentThread);
|
||||
currentThread->waitingForMutex = nullptr;
|
||||
failedAttempts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OSLockMutex(OSMutex* mutex)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
OSTestThreadCancelInternal();
|
||||
OSLockMutexInternal(mutex);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
bool OSTryLockMutex(OSMutex* mutex)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
__OSLockScheduler();
|
||||
OSTestThreadCancelInternal();
|
||||
if (mutex->owner == nullptr)
|
||||
{
|
||||
// acquire lock
|
||||
mutex->owner = currentThread;
|
||||
cemu_assert_debug(mutex->lockCount == 0);
|
||||
mutex->lockCount = 1;
|
||||
// cemu_assert_debug(mutex->next == nullptr && mutex->prev == nullptr); -> not zero initialized
|
||||
currentThread->mutexQueue.addMutex(mutex);
|
||||
// currentThread->cancelState = currentThread->cancelState | 0x10000;
|
||||
}
|
||||
else if (mutex->owner == currentThread)
|
||||
{
|
||||
mutex->lockCount = mutex->lockCount + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
__OSUnlockScheduler();
|
||||
return false;
|
||||
}
|
||||
__OSUnlockScheduler();
|
||||
return true;
|
||||
}
|
||||
|
||||
void OSUnlockMutexInternal(OSMutex* mutex)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
cemu_assert_debug(mutex->owner == currentThread);
|
||||
cemu_assert_debug(mutex->lockCount > 0);
|
||||
mutex->lockCount = mutex->lockCount - 1;
|
||||
if (mutex->lockCount == 0)
|
||||
{
|
||||
currentThread->mutexQueue.removeMutex(mutex);
|
||||
mutex->owner = nullptr;
|
||||
if (!mutex->threadQueue.isEmpty())
|
||||
mutex->threadQueue.wakeupSingleThreadWaitQueue(true);
|
||||
}
|
||||
// currentThread->cancelState = currentThread->cancelState & ~0x10000;
|
||||
}
|
||||
|
||||
void OSUnlockMutex(OSMutex* mutex)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
OSUnlockMutexInternal(mutex);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
/************* OSCond ************/
|
||||
|
||||
void OSInitCond(OSCond* cond)
|
||||
{
|
||||
cond->magic = 0x634e6456;
|
||||
cond->userData = nullptr;
|
||||
cond->ukn08 = 0;
|
||||
OSInitThreadQueueEx(&cond->threadQueue, cond);
|
||||
}
|
||||
|
||||
void OSInitCondEx(OSCond* cond, void* userData)
|
||||
{
|
||||
OSInitCond(cond);
|
||||
cond->userData = userData;
|
||||
}
|
||||
|
||||
void OSSignalCond(OSCond* cond)
|
||||
{
|
||||
OSWakeupThread(&cond->threadQueue);
|
||||
}
|
||||
|
||||
void OSWaitCond(OSCond* cond, OSMutex* mutex)
|
||||
{
|
||||
// seen in Bayonetta 2
|
||||
// releases the mutex while waiting for the condition to be signaled
|
||||
__OSLockScheduler();
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
cemu_assert_debug(mutex->owner == currentThread);
|
||||
sint32 prevLockCount = mutex->lockCount;
|
||||
// unlock mutex
|
||||
mutex->lockCount = 0;
|
||||
currentThread->mutexQueue.removeMutex(mutex);
|
||||
mutex->owner = nullptr;
|
||||
if (!mutex->threadQueue.isEmpty())
|
||||
mutex->threadQueue.wakeupEntireWaitQueue(false);
|
||||
// wait on condition
|
||||
cond->threadQueue.queueAndWait(currentThread);
|
||||
// reacquire mutex
|
||||
OSLockMutexInternal(mutex);
|
||||
mutex->lockCount = prevLockCount;
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
/************* OSSemaphore ************/
|
||||
|
||||
void OSInitSemaphoreEx(OSSemaphore* semaphore, sint32 initialCount, void* userData)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
semaphore->magic = 0x73506852;
|
||||
semaphore->userData = userData;
|
||||
semaphore->ukn08 = 0;
|
||||
semaphore->count = initialCount;
|
||||
OSInitThreadQueueEx(&semaphore->threadQueue, semaphore);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void OSInitSemaphore(OSSemaphore* semaphore, sint32 initialCount)
|
||||
{
|
||||
OSInitSemaphoreEx(semaphore, initialCount, nullptr);
|
||||
}
|
||||
|
||||
sint32 OSWaitSemaphoreInternal(OSSemaphore* semaphore)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
while (true)
|
||||
{
|
||||
sint32 prevCount = semaphore->count;
|
||||
if (prevCount > 0)
|
||||
{
|
||||
semaphore->count = prevCount - 1;
|
||||
return prevCount;
|
||||
}
|
||||
semaphore->threadQueue.queueAndWait(OSGetCurrentThread());
|
||||
}
|
||||
}
|
||||
|
||||
sint32 OSWaitSemaphore(OSSemaphore* semaphore)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
sint32 r = OSWaitSemaphoreInternal(semaphore);
|
||||
__OSUnlockScheduler();
|
||||
return r;
|
||||
}
|
||||
|
||||
sint32 OSTryWaitSemaphore(OSSemaphore* semaphore)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
sint32 prevCount = semaphore->count;
|
||||
if (prevCount > 0)
|
||||
{
|
||||
semaphore->count = prevCount - 1;
|
||||
}
|
||||
__OSUnlockScheduler();
|
||||
return prevCount;
|
||||
}
|
||||
|
||||
sint32 OSSignalSemaphoreInternal(OSSemaphore* semaphore, bool reschedule)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
sint32 prevCount = semaphore->count;
|
||||
semaphore->count = prevCount + 1;
|
||||
semaphore->threadQueue.wakeupEntireWaitQueue(reschedule);
|
||||
return prevCount;
|
||||
}
|
||||
|
||||
sint32 OSSignalSemaphore(OSSemaphore* semaphore)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
sint32 r = OSSignalSemaphoreInternal(semaphore, true);
|
||||
__OSUnlockScheduler();
|
||||
return r;
|
||||
}
|
||||
|
||||
sint32 OSGetSemaphoreCount(OSSemaphore* semaphore)
|
||||
{
|
||||
// seen in Assassin's Creed 4
|
||||
__OSLockScheduler();
|
||||
sint32 currentCount = semaphore->count;
|
||||
__OSUnlockScheduler();
|
||||
return currentCount;
|
||||
}
|
||||
|
||||
/************* OSFastMutex ************/
|
||||
|
||||
void OSFastMutex_Init(OSFastMutex* fastMutex, void* userData)
|
||||
{
|
||||
fastMutex->magic = 0x664d7458;
|
||||
fastMutex->userData = userData;
|
||||
fastMutex->owner = nullptr;
|
||||
fastMutex->lockCount = 0;
|
||||
fastMutex->contendedState = 0;
|
||||
fastMutex->threadQueueSmall.head = nullptr;
|
||||
fastMutex->threadQueueSmall.tail = nullptr;
|
||||
fastMutex->ownedLink.next = nullptr;
|
||||
fastMutex->ownedLink.prev = nullptr;
|
||||
fastMutex->contendedLink.next = nullptr;
|
||||
fastMutex->contendedLink.prev = nullptr;
|
||||
}
|
||||
|
||||
FSpinlock g_fastMutexSpinlock;
|
||||
|
||||
void _OSFastMutex_AcquireContention(OSFastMutex* fastMutex)
|
||||
{
|
||||
g_fastMutexSpinlock.acquire();
|
||||
}
|
||||
|
||||
void _OSFastMutex_ReleaseContention(OSFastMutex* fastMutex)
|
||||
{
|
||||
g_fastMutexSpinlock.release();
|
||||
}
|
||||
|
||||
void OSFastMutex_LockInternal(OSFastMutex* fastMutex)
|
||||
{
|
||||
cemu_assert_debug(!__OSHasSchedulerLock());
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
_OSFastMutex_AcquireContention(fastMutex);
|
||||
while (true)
|
||||
{
|
||||
if (fastMutex->owner.atomic_compare_exchange(nullptr, currentThread))//(fastMutex->owner == nullptr)
|
||||
{
|
||||
// acquire lock
|
||||
cemu_assert_debug(fastMutex->owner == currentThread);
|
||||
cemu_assert_debug(fastMutex->lockCount == 0);
|
||||
fastMutex->lockCount = 1;
|
||||
|
||||
// todo - add to thread owned fast mutex queue
|
||||
break;
|
||||
}
|
||||
else if (fastMutex->owner == currentThread)
|
||||
{
|
||||
fastMutex->lockCount = fastMutex->lockCount + 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentThread->waitingForFastMutex = fastMutex;
|
||||
__OSLockScheduler();
|
||||
fastMutex->threadQueueSmall.queueOnly(currentThread);
|
||||
_OSFastMutex_ReleaseContention(fastMutex);
|
||||
PPCCore_switchToSchedulerWithLock();
|
||||
currentThread->waitingForFastMutex = nullptr;
|
||||
__OSUnlockScheduler();
|
||||
_OSFastMutex_AcquireContention(fastMutex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_OSFastMutex_ReleaseContention(fastMutex);
|
||||
}
|
||||
|
||||
void OSFastMutex_Lock(OSFastMutex* fastMutex)
|
||||
{
|
||||
OSFastMutex_LockInternal(fastMutex);
|
||||
}
|
||||
|
||||
bool OSFastMutex_TryLock(OSFastMutex* fastMutex)
|
||||
{
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
_OSFastMutex_AcquireContention(fastMutex);
|
||||
if (fastMutex->owner.atomic_compare_exchange(nullptr, currentThread))
|
||||
{
|
||||
// acquire lock
|
||||
cemu_assert_debug(fastMutex->owner == currentThread);
|
||||
cemu_assert_debug(fastMutex->lockCount == 0);
|
||||
fastMutex->lockCount = 1;
|
||||
|
||||
// todo - add to thread owned fast mutex queue
|
||||
}
|
||||
else if (fastMutex->owner == currentThread)
|
||||
{
|
||||
fastMutex->lockCount = fastMutex->lockCount + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_OSFastMutex_ReleaseContention(fastMutex);
|
||||
return false;
|
||||
}
|
||||
_OSFastMutex_ReleaseContention(fastMutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OSFastMutex_UnlockInternal(OSFastMutex* fastMutex)
|
||||
{
|
||||
cemu_assert_debug(!__OSHasSchedulerLock());
|
||||
OSThread_t* currentThread = OSGetCurrentThread();
|
||||
_OSFastMutex_AcquireContention(fastMutex);
|
||||
if (fastMutex->owner != currentThread)
|
||||
{
|
||||
// seen in Paper Mario Color Splash
|
||||
//forceLog_printf("OSFastMutex_Unlock() called on mutex which is not owned by current thread");
|
||||
_OSFastMutex_ReleaseContention(fastMutex);
|
||||
return;
|
||||
}
|
||||
cemu_assert_debug(fastMutex->lockCount > 0);
|
||||
fastMutex->lockCount = fastMutex->lockCount - 1;
|
||||
if (fastMutex->lockCount == 0)
|
||||
{
|
||||
// set owner to null
|
||||
if (!fastMutex->owner.atomic_compare_exchange(currentThread, nullptr))
|
||||
{
|
||||
cemu_assert_debug(false); // should never happen
|
||||
}
|
||||
if (!fastMutex->threadQueueSmall.isEmpty())
|
||||
{
|
||||
__OSLockScheduler();
|
||||
fastMutex->threadQueueSmall.wakeupSingleThreadWaitQueue(false);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
}
|
||||
_OSFastMutex_ReleaseContention(fastMutex);
|
||||
}
|
||||
|
||||
void OSFastMutex_Unlock(OSFastMutex* fastMutex)
|
||||
{
|
||||
//__OSLockScheduler();
|
||||
OSFastMutex_UnlockInternal(fastMutex);
|
||||
//__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
/************* OSFastCond ************/
|
||||
|
||||
void OSFastCond_Init(OSFastCond* fastCond, void* userData)
|
||||
{
|
||||
fastCond->magic = 0x664e6456;
|
||||
fastCond->userData = userData;
|
||||
fastCond->ukn08 = 0;
|
||||
OSInitThreadQueueEx(&fastCond->threadQueue, fastCond);
|
||||
}
|
||||
|
||||
void OSFastCond_Wait(OSFastCond* fastCond, OSFastMutex* fastMutex)
|
||||
{
|
||||
// releases the mutex while waiting for the condition to be signaled
|
||||
__OSLockScheduler();
|
||||
cemu_assert_debug(fastMutex->owner == OSGetCurrentThread());
|
||||
sint32 prevLockCount = fastMutex->lockCount;
|
||||
// unlock mutex
|
||||
fastMutex->lockCount = 0;
|
||||
fastMutex->owner = nullptr;
|
||||
if (!fastMutex->threadQueueSmall.isEmpty())
|
||||
fastMutex->threadQueueSmall.wakeupEntireWaitQueue(false);
|
||||
// wait on condition
|
||||
fastCond->threadQueue.queueAndWait(OSGetCurrentThread());
|
||||
// reacquire mutex
|
||||
__OSUnlockScheduler();
|
||||
OSFastMutex_LockInternal(fastMutex);
|
||||
fastMutex->lockCount = prevLockCount;
|
||||
}
|
||||
|
||||
void OSFastCond_Signal(OSFastCond* fastCond)
|
||||
{
|
||||
OSWakeupThread(&fastCond->threadQueue);
|
||||
}
|
||||
|
||||
/************* init ************/
|
||||
|
||||
void InitializeConcurrency()
|
||||
{
|
||||
OSInitEvent(g_rendezvousEvent.GetPtr(), OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_AUTO);
|
||||
|
||||
// OSEvent
|
||||
cafeExportRegister("coreinit", OSInitEvent, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSInitEventEx, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSResetEvent, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSWaitEvent, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSWaitEventWithTimeout, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSSignalEvent, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSSignalEventAll, LogType::ThreadSync);
|
||||
|
||||
// OSRendezvous
|
||||
cafeExportRegister("coreinit", OSInitRendezvous, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSWaitRendezvous, LogType::ThreadSync);
|
||||
|
||||
// OSMutex
|
||||
cafeExportRegister("coreinit", OSInitMutex, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSInitMutexEx, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSLockMutex, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSTryLockMutex, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSUnlockMutex, LogType::ThreadSync);
|
||||
|
||||
// OSCond
|
||||
cafeExportRegister("coreinit", OSInitCond, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSInitCondEx, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSSignalCond, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSWaitCond, LogType::ThreadSync);
|
||||
|
||||
// OSSemaphore
|
||||
cafeExportRegister("coreinit", OSInitSemaphore, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSInitSemaphoreEx, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSWaitSemaphore, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSTryWaitSemaphore, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSSignalSemaphore, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSGetSemaphoreCount, LogType::ThreadSync);
|
||||
|
||||
// OSFastMutex
|
||||
cafeExportRegister("coreinit", OSFastMutex_Init, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSFastMutex_Lock, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSFastMutex_TryLock, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSFastMutex_Unlock, LogType::ThreadSync);
|
||||
|
||||
// OSFastCond
|
||||
cafeExportRegister("coreinit", OSFastCond_Init, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSFastCond_Wait, LogType::ThreadSync);
|
||||
cafeExportRegister("coreinit", OSFastCond_Signal, LogType::ThreadSync);
|
||||
}
|
||||
|
||||
};
|
||||
40
src/Cafe/OS/libs/coreinit/coreinit_SysHeap.cpp
Normal file
40
src/Cafe/OS/libs/coreinit/coreinit_SysHeap.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_SysHeap.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
coreinit::MEMHeapHandle _sysHeapHandle = MPTR_NULL;
|
||||
sint32 _sysHeapAllocCounter = 0;
|
||||
sint32 _sysHeapFreeCounter = 0;
|
||||
|
||||
void* OSAllocFromSystem(uint32 size, uint32 alignment)
|
||||
{
|
||||
_sysHeapAllocCounter++;
|
||||
return coreinit::MEMAllocFromExpHeapEx(_sysHeapHandle, size, alignment);
|
||||
}
|
||||
|
||||
void export_OSAllocFromSystem(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(size, 0);
|
||||
ppcDefineParamS32(alignment, 1);
|
||||
MEMPTR<void> mem = OSAllocFromSystem(size, alignment);
|
||||
forceLogDebug_printf("OSAllocFromSystem(0x%x, %d) -> 0x%08x", size, alignment, mem.GetMPTR());
|
||||
osLib_returnFromFunction(hCPU, mem.GetMPTR());
|
||||
}
|
||||
|
||||
void InitSysHeap()
|
||||
{
|
||||
uint32 sysHeapSize = 8 * 1024 * 1024; // actual size is unknown
|
||||
MEMPTR<void> heapBaseAddress = memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sysHeapSize, 0x1000));
|
||||
_sysHeapHandle = coreinit::MEMCreateExpHeapEx(heapBaseAddress.GetPtr(), sysHeapSize, MEM_HEAP_OPTION_THREADSAFE);
|
||||
_sysHeapAllocCounter = 0;
|
||||
_sysHeapFreeCounter = 0;
|
||||
}
|
||||
|
||||
void InitializeSysHeap()
|
||||
{
|
||||
osLib_addFunction("coreinit", "OSAllocFromSystem", export_OSAllocFromSystem);
|
||||
}
|
||||
|
||||
}
|
||||
8
src/Cafe/OS/libs/coreinit/coreinit_SysHeap.h
Normal file
8
src/Cafe/OS/libs/coreinit/coreinit_SysHeap.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitSysHeap();
|
||||
|
||||
void InitializeSysHeap();
|
||||
}
|
||||
25
src/Cafe/OS/libs/coreinit/coreinit_SystemInfo.cpp
Normal file
25
src/Cafe/OS/libs/coreinit/coreinit_SystemInfo.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_SystemInfo.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
SysAllocator<OSSystemInfo> g_system_info;
|
||||
|
||||
const OSSystemInfo& OSGetSystemInfo()
|
||||
{
|
||||
return *g_system_info.GetPtr();
|
||||
}
|
||||
|
||||
void InitializeSystemInfo()
|
||||
{
|
||||
cemu_assert(ppcCyclesSince2000 != 0);
|
||||
g_system_info->busClock = ESPRESSO_BUS_CLOCK;
|
||||
g_system_info->coreClock = ESPRESSO_CORE_CLOCK;
|
||||
g_system_info->ticksSince2000 = ESPRESSO_CORE_CLOCK_TO_TIMER_CLOCK(ppcCyclesSince2000);
|
||||
g_system_info->l2cacheSize[0] = 512*1024; // 512KB // 512KB
|
||||
g_system_info->l2cacheSize[1] = 2*1024*1924; // 2MB
|
||||
g_system_info->l2cacheSize[2] = 512*1024; // 512KB
|
||||
g_system_info->coreClockToBusClockRatio = 5;
|
||||
cafeExportRegister("coreinit", OSGetSystemInfo, LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
21
src/Cafe/OS/libs/coreinit/coreinit_SystemInfo.h
Normal file
21
src/Cafe/OS/libs/coreinit/coreinit_SystemInfo.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
struct OSSystemInfo
|
||||
{
|
||||
uint32be busClock;
|
||||
uint32be coreClock;
|
||||
uint64be ticksSince2000;
|
||||
uint32be l2cacheSize[PPC_CORE_COUNT];
|
||||
uint32be coreClockToBusClockRatio;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSSystemInfo) == 0x20);
|
||||
|
||||
const OSSystemInfo& OSGetSystemInfo();
|
||||
|
||||
void InitializeSystemInfo();
|
||||
};
|
||||
|
||||
1368
src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp
Normal file
1368
src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp
Normal file
File diff suppressed because it is too large
Load diff
605
src/Cafe/OS/libs/coreinit/coreinit_Thread.h
Normal file
605
src/Cafe/OS/libs/coreinit/coreinit_Thread.h
Normal file
|
|
@ -0,0 +1,605 @@
|
|||
#pragma once
|
||||
#include "Cafe/HW/Espresso/Const.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Scheduler.h"
|
||||
|
||||
#define OS_CONTEXT_MAGIC_0 'OSCo'
|
||||
#define OS_CONTEXT_MAGIC_1 'ntxt'
|
||||
|
||||
struct OSThread_t;
|
||||
|
||||
struct OSContextRegFPSCR_t
|
||||
{
|
||||
// FPSCR is a 32bit register but it's stored as a 64bit double
|
||||
/* +0x00 */ uint32be padding;
|
||||
/* +0x04 */ uint32be fpscr;
|
||||
};
|
||||
|
||||
struct OSContext_t
|
||||
{
|
||||
/* +0x000 */ betype<uint32> magic0;
|
||||
/* +0x004 */ betype<uint32> magic1;
|
||||
/* +0x008 */ uint32 gpr[32];
|
||||
/* +0x088 */ uint32 cr;
|
||||
/* +0x08C */ uint32 lr;
|
||||
/* +0x090 */ uint32 ctr;
|
||||
/* +0x094 */ uint32 xer;
|
||||
/* +0x098 */ uint32 srr0;
|
||||
/* +0x09C */ uint32 srr1;
|
||||
/* +0x0A0 */ uint32 dsi_dsisr;
|
||||
/* +0x0A4 */ uint32 dsi_dar;
|
||||
/* +0x0A8 */ uint32 ukn0A8;
|
||||
/* +0x0AC */ uint32 ukn0AC;
|
||||
/* +0x0B0 */ OSContextRegFPSCR_t fpscr;
|
||||
/* +0x0B8 */ uint64be fp_ps0[32];
|
||||
/* +0x1B8 */ uint16be boostCount;
|
||||
/* +0x1BA */ uint16 state; // OS_CONTEXT_STATE_*
|
||||
/* +0x1BC */ uint32 gqr[8]; // GQR/UGQR
|
||||
/* +0x1DC */ uint32be upir; // set to current core index
|
||||
/* +0x1E0 */ uint64be fp_ps1[32];
|
||||
/* +0x2E0 */ uint64 uknTime2E0;
|
||||
/* +0x2E8 */ uint64 uknTime2E8;
|
||||
/* +0x2F0 */ uint64 uknTime2F0;
|
||||
/* +0x2F8 */ uint64 uknTime2F8;
|
||||
/* +0x300 */ uint32 error; // returned by __gh_errno_ptr() (used by socketlasterr)
|
||||
/* +0x304 */ uint32be affinity;
|
||||
/* +0x308 */ uint32 ukn0308;
|
||||
/* +0x30C */ uint32 ukn030C;
|
||||
/* +0x310 */ uint32 ukn0310;
|
||||
/* +0x314 */ uint32 ukn0314;
|
||||
/* +0x318 */ uint32 ukn0318;
|
||||
/* +0x31C */ uint32 ukn031C;
|
||||
|
||||
bool checkMagic()
|
||||
{
|
||||
return magic0 == (uint32)OS_CONTEXT_MAGIC_0 && magic1 == (uint32)OS_CONTEXT_MAGIC_1;
|
||||
}
|
||||
|
||||
bool hasCoreAffinitySet(uint32 coreIndex) const
|
||||
{
|
||||
return (((uint32)affinity >> coreIndex) & 1) != 0;
|
||||
}
|
||||
|
||||
void setAffinity(uint32 mask)
|
||||
{
|
||||
affinity = mask & 7;
|
||||
}
|
||||
|
||||
uint32 getAffinity() const
|
||||
{
|
||||
return affinity;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSContext_t) == 0x320);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x000 | +0x3A0 */ uint32 ukn000[0x68 / 4];
|
||||
/* +0x068 | +0x408 */ MEMPTR<void> eh_globals;
|
||||
/* +0x06C | +0x40C */ uint32 eh_mem_manage[9]; // struct
|
||||
/* +0x090 | +0x430 */ MPTR eh_store_globals[6];
|
||||
/* +0x0A8 | +0x448 */ MPTR eh_store_globals_tdeh[76];
|
||||
}crt_t; // size: 0x1D8
|
||||
|
||||
static_assert(sizeof(crt_t) == 0x1D8, "");
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
/********* OSThreadQueue *********/
|
||||
|
||||
struct OSThreadLink
|
||||
{
|
||||
MEMPTR<struct OSThread_t> next;
|
||||
MEMPTR<struct OSThread_t> prev;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSThreadLink) == 8);
|
||||
|
||||
struct OSThreadQueueInternal
|
||||
{
|
||||
MEMPTR<OSThread_t> head;
|
||||
MEMPTR<OSThread_t> tail;
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
cemu_assert_debug((!head.IsNull() == !tail.IsNull()) || (head.IsNull() == tail.IsNull()));
|
||||
return head.IsNull();
|
||||
}
|
||||
|
||||
void addThread(OSThread_t* thread, OSThreadLink* threadLink);
|
||||
void addThreadByPriority(OSThread_t* thread, OSThreadLink* threadLink);
|
||||
void removeThread(OSThread_t* thread, OSThreadLink* threadLink);
|
||||
|
||||
// puts the thread on the waiting queue and changes state to WAITING
|
||||
// relinquishes timeslice
|
||||
// always uses thread->waitQueueLink
|
||||
void queueAndWait(OSThread_t* thread);
|
||||
void queueOnly(OSThread_t* thread);
|
||||
|
||||
// counterparts for queueAndWait
|
||||
void cancelWait(OSThread_t* thread);
|
||||
void wakeupEntireWaitQueue(bool reschedule);
|
||||
void wakeupSingleThreadWaitQueue(bool reschedule);
|
||||
|
||||
private:
|
||||
OSThread_t* takeFirstFromQueue(size_t linkOffset)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
if (head == nullptr)
|
||||
return nullptr;
|
||||
OSThread_t* thread = head.GetPtr();
|
||||
OSThreadLink* link = _getThreadLink(thread, linkOffset);
|
||||
removeThread(thread, link);
|
||||
return thread;
|
||||
}
|
||||
|
||||
static size_t getLinkOffset(OSThread_t* thread, OSThreadLink* threadLink)
|
||||
{
|
||||
cemu_assert_debug((void*)threadLink >= (void*)thread && (void*)threadLink < (void*)((uint8*)thread + 0x680));
|
||||
return (uint8*)threadLink - (uint8*)thread;
|
||||
}
|
||||
|
||||
static OSThreadLink* _getThreadLink(OSThread_t* thread, size_t linkOffset)
|
||||
{
|
||||
return (OSThreadLink*)((uint8*)thread + linkOffset);
|
||||
}
|
||||
|
||||
void _debugCheckChain(OSThread_t* thread, OSThreadLink* threadLink)
|
||||
{
|
||||
#ifndef PUBLIC_RELEASE
|
||||
cemu_assert_debug(tail.IsNull() == head.IsNull());
|
||||
size_t linkOffset = getLinkOffset(thread, threadLink);
|
||||
// expects thread to be in the chain
|
||||
OSThread_t* threadItr = head.GetPtr();
|
||||
while (threadItr)
|
||||
{
|
||||
if (threadItr == thread)
|
||||
return;
|
||||
threadItr = _getThreadLink(threadItr, linkOffset)->next.GetPtr();
|
||||
}
|
||||
cemu_assert_debug(false); // thread not in list!
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSThreadQueueInternal) == 0x8);
|
||||
|
||||
struct OSThreadQueueSmall : public OSThreadQueueInternal
|
||||
{
|
||||
// no extra members
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSThreadQueueSmall) == 8);
|
||||
static_assert(offsetof(OSThreadQueueSmall, head) == 0x0);
|
||||
static_assert(offsetof(OSThreadQueueSmall, tail) == 0x4);
|
||||
|
||||
struct OSThreadQueue : public OSThreadQueueInternal
|
||||
{
|
||||
MEMPTR<void> userData;
|
||||
uint32be ukn0C;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSThreadQueue) == 0x10);
|
||||
static_assert(offsetof(OSThreadQueue, head) == 0x0);
|
||||
static_assert(offsetof(OSThreadQueue, tail) == 0x4);
|
||||
static_assert(offsetof(OSThreadQueue, userData) == 0x8);
|
||||
static_assert(offsetof(OSThreadQueue, ukn0C) == 0xC);
|
||||
|
||||
/********* OSMutex *********/
|
||||
|
||||
struct OSMutex
|
||||
{
|
||||
/* +0x00 */ uint32 magic;
|
||||
/* +0x04 */ MEMPTR<void> userData;
|
||||
/* +0x08 */ uint32be ukn08;
|
||||
/* +0x0C */ OSThreadQueue threadQueue;
|
||||
/* +0x1C */ MEMPTR<OSThread_t> owner;
|
||||
/* +0x20 */ sint32be lockCount;
|
||||
/* +0x24 */ MEMPTR<OSMutex> next;
|
||||
/* +0x28 */ MEMPTR<OSMutex> prev;
|
||||
|
||||
}; // size: 0x2C
|
||||
|
||||
static_assert(sizeof(OSMutex) == 0x2C);
|
||||
|
||||
struct OSMutexQueue
|
||||
{
|
||||
MEMPTR<OSMutex> head;
|
||||
MEMPTR<OSMutex> tail;
|
||||
MEMPTR<void> ukn08;
|
||||
uint32be ukn0C;
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
cemu_assert_debug((!head.IsNull() == !tail.IsNull()) || (head.IsNull() == tail.IsNull()));
|
||||
return head.IsNull();
|
||||
}
|
||||
|
||||
void addMutex(OSMutex* mutex)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
// insert at end
|
||||
if (tail.IsNull())
|
||||
{
|
||||
mutex->next = nullptr;
|
||||
mutex->prev = nullptr;
|
||||
head = mutex;
|
||||
tail = mutex;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail->next = mutex;
|
||||
mutex->prev = tail;
|
||||
mutex->next = nullptr;
|
||||
tail = mutex;
|
||||
}
|
||||
}
|
||||
|
||||
void removeMutex(OSMutex* mutex)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
cemu_assert_debug(!head.IsNull() && !tail.IsNull());
|
||||
if (mutex->prev)
|
||||
mutex->prev->next = mutex->next;
|
||||
else
|
||||
head = mutex->next;
|
||||
if (mutex->next)
|
||||
mutex->next->prev = mutex->prev;
|
||||
else
|
||||
tail = mutex->prev;
|
||||
mutex->next = nullptr;
|
||||
mutex->prev = nullptr;
|
||||
}
|
||||
|
||||
OSMutex* getFirst()
|
||||
{
|
||||
return head.GetPtr();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSMutexQueue) == 0x10);
|
||||
|
||||
/********* OSFastMutex *********/
|
||||
|
||||
struct OSFastMutexLink
|
||||
{
|
||||
/* +0x00 */ MEMPTR<struct OSMutex> next;
|
||||
/* +0x04 */ MEMPTR<struct OSMutex> prev;
|
||||
};
|
||||
|
||||
struct OSFastMutex
|
||||
{
|
||||
/* +0x00 */ uint32be magic;
|
||||
/* +0x04 */ MEMPTR<void> userData;
|
||||
/* +0x08 */ uint32be contendedState; // tracks current contention state
|
||||
/* +0x0C */ OSThreadQueueSmall threadQueueSmall;
|
||||
/* +0x14 */ OSFastMutexLink ownedLink; // part of thread->fastMutexOwnedQueue
|
||||
/* +0x1C */ MEMPTR<OSThread_t> owner;
|
||||
/* +0x20 */ uint32be lockCount;
|
||||
/* +0x24 */ OSFastMutexLink contendedLink;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSFastMutex) == 0x2C);
|
||||
|
||||
/********* OSEvent *********/
|
||||
|
||||
struct OSEvent
|
||||
{
|
||||
enum class EVENT_MODE : uint32
|
||||
{
|
||||
MODE_MANUAL = 0,
|
||||
MODE_AUTO = 1,
|
||||
};
|
||||
|
||||
enum class EVENT_STATE : uint32
|
||||
{
|
||||
STATE_NOT_SIGNALED = 0,
|
||||
STATE_SIGNALED = 1
|
||||
};
|
||||
|
||||
/* +0x00 */ uint32be magic; // 'eVnT'
|
||||
/* +0x04 */ MEMPTR<void> userData;
|
||||
/* +0x08 */ uint32be ukn08;
|
||||
/* +0x0C */ betype<EVENT_STATE> state; // 0 -> not signaled, 1 -> signaled
|
||||
/* +0x10 */ OSThreadQueue threadQueue;
|
||||
/* +0x20 */ betype<EVENT_MODE> mode;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSEvent) == 0x24);
|
||||
|
||||
/********* OSRendezvous *********/
|
||||
|
||||
struct OSRendezvous
|
||||
{
|
||||
/* +0x00 */ uint32be coreHit[3];
|
||||
/* +0x0C */ MEMPTR<void> userData;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSRendezvous) == 0x10);
|
||||
|
||||
/********* OSCond *********/
|
||||
|
||||
struct OSCond
|
||||
{
|
||||
uint32be magic;
|
||||
MEMPTR<void> userData;
|
||||
uint32be ukn08;
|
||||
OSThreadQueue threadQueue;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSCond) == 0x1C);
|
||||
|
||||
/********* OSSemaphore *********/
|
||||
|
||||
struct OSSemaphore
|
||||
{
|
||||
uint32be magic;
|
||||
MEMPTR<void> userData;
|
||||
uint32be ukn08;
|
||||
sint32be count;
|
||||
OSThreadQueue threadQueue;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSSemaphore) == 0x20);
|
||||
|
||||
/********* OSFastCond *********/
|
||||
|
||||
struct OSFastCond
|
||||
{
|
||||
uint32be magic;
|
||||
MEMPTR<void> userData;
|
||||
uint32be ukn08;
|
||||
OSThreadQueue threadQueue;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSFastCond) == 0x1C);
|
||||
|
||||
};
|
||||
|
||||
struct OSThread_t
|
||||
{
|
||||
enum class THREAD_TYPE : uint32
|
||||
{
|
||||
TYPE_DRIVER = 0,
|
||||
TYPE_IO = 1,
|
||||
TYPE_APP = 2
|
||||
};
|
||||
|
||||
enum class THREAD_STATE : uint8
|
||||
{
|
||||
STATE_NONE = 0,
|
||||
STATE_READY = 1,
|
||||
STATE_RUNNING = 2,
|
||||
STATE_WAITING = 4,
|
||||
STATE_MORIBUND = 8,
|
||||
};
|
||||
|
||||
enum ATTR_BIT : uint32
|
||||
{
|
||||
ATTR_AFFINITY_CORE0 = 0x1,
|
||||
ATTR_AFFINITY_CORE1 = 0x2,
|
||||
ATTR_AFFINITY_CORE2 = 0x4,
|
||||
ATTR_DETACHED = 0x8,
|
||||
// more flags?
|
||||
};
|
||||
|
||||
enum REQUEST_FLAG_BIT : uint32
|
||||
{
|
||||
REQUEST_FLAG_NONE = 0,
|
||||
REQUEST_FLAG_SUSPEND = 1,
|
||||
REQUEST_FLAG_CANCEL = 2,
|
||||
};
|
||||
|
||||
static sint32 GetTypePriorityBase(THREAD_TYPE threadType)
|
||||
{
|
||||
if (threadType == OSThread_t::THREAD_TYPE::TYPE_DRIVER)
|
||||
return 0;
|
||||
else if (threadType == OSThread_t::THREAD_TYPE::TYPE_IO)
|
||||
return 32;
|
||||
else if (threadType == OSThread_t::THREAD_TYPE::TYPE_APP)
|
||||
return 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* +0x000 */ OSContext_t context;
|
||||
/* +0x320 */ uint32be magic; // 'tHrD'
|
||||
/* +0x324 */ betype<THREAD_STATE> state;
|
||||
/* +0x325 */ uint8 attr;
|
||||
/* +0x326 */ uint16be id; // Warriors Orochi 3 uses this to identify threads. Seems like this is always set to 0x8000 ?
|
||||
/* +0x328 */ betype<sint32> suspendCounter;
|
||||
/* +0x32C */ sint32be effectivePriority; // effective priority (lower is higher)
|
||||
/* +0x330 */ sint32be basePriority; // base priority (lower is higher)
|
||||
/* +0x334 */ uint32be exitValue; // set by OSExitThread() or by returning from the thread entrypoint
|
||||
|
||||
/* +0x338 */ MEMPTR<coreinit::OSThreadQueue> currentRunQueue[Espresso::CORE_COUNT]; // points to the OSThreadQueue on which this thread is (null if not in queue)
|
||||
/* +0x344 */ coreinit::OSThreadLink linkRun[Espresso::CORE_COUNT];
|
||||
|
||||
// general wait queue / thread dependency queue
|
||||
/* +0x35C */ MEMPTR<coreinit::OSThreadQueueInternal> currentWaitQueue; // shared between OSThreadQueue and OSThreadQueueSmall
|
||||
/* +0x360 */ coreinit::OSThreadLink waitQueueLink;
|
||||
|
||||
/* +0x368 */ coreinit::OSThreadQueue joinQueue;
|
||||
|
||||
/* +0x378 */ MEMPTR<coreinit::OSMutex> waitingForMutex; // set when thread is waiting for OSMutex to unlock
|
||||
/* +0x37C */ coreinit::OSMutexQueue mutexQueue;
|
||||
|
||||
/* +0x38C */ coreinit::OSThreadLink activeThreadChain; // queue of active threads (g_activeThreadQueue)
|
||||
|
||||
/* +0x394 */ MPTR_UINT8 stackBase; // upper limit of stack
|
||||
/* +0x398 */ MPTR_UINT32 stackEnd; // lower limit of stack
|
||||
|
||||
/* +0x39C */ MPTR entrypoint;
|
||||
/* +0x3A0 */ crt_t crt;
|
||||
|
||||
/* +0x578 */ sint32 alarmRelatedUkn;
|
||||
/* +0x57C */ std::array<MEMPTR<void>, 16> specificArray;
|
||||
/* +0x5BC */ betype<THREAD_TYPE> type;
|
||||
/* +0x5C0 */ MEMPTR<char> threadName;
|
||||
/* +0x5C4 */ MPTR waitAlarm; // used only by OSWaitEventWithTimeout/OSSignalEvent ?
|
||||
|
||||
/* +0x5C8 */ uint32 userStackPointer;
|
||||
|
||||
/* +0x5CC */ MEMPTR<void> cleanupCallback2;
|
||||
/* +0x5D0 */ //MPTR deallocator;
|
||||
MEMPTR<void> deallocatorFunc;
|
||||
|
||||
/* +0x5D4 */ uint32 stateFlags; // 0x5D4 | various flags? Controls if canceling/suspension is allowed (at cancel points) or not? If 1 -> Cancel/Suspension not allowed, if 0 -> Cancel/Suspension allowed
|
||||
/* +0x5D8 */ betype<REQUEST_FLAG_BIT> requestFlags;
|
||||
/* +0x5DC */ sint32 pendingSuspend;
|
||||
/* +0x5E0 */ sint32 suspendResult;
|
||||
/* +0x5E4 */ coreinit::OSThreadQueue suspendQueue;
|
||||
/* +0x5F4 */ uint32 _padding5F4;
|
||||
/* +0x5F8 */ uint64be quantumTicks;
|
||||
/* +0x600 */ uint64 coretimeSumQuantumStart;
|
||||
|
||||
/* +0x608 */ uint64be wakeUpCount; // number of times the thread entered running state
|
||||
/* +0x610 */ uint64be totalCycles; // sum of cycles this thread was active since it was created
|
||||
/* +0x618 */ uint64be wakeUpTime; // time when thread first became active (Should only be set once(?) but we currently set this on every timeslice since thats more useful for debugging)
|
||||
/* +0x620 */ uint64 wakeTimeRelatedUkn1;
|
||||
/* +0x628 */ uint64 wakeTimeRelatedUkn2;
|
||||
|
||||
// set via OSSetExceptionCallback
|
||||
/* +0x630 */ MPTR ukn630Callback[Espresso::CORE_COUNT];
|
||||
/* +0x63C */ MPTR ukn63CCallback[Espresso::CORE_COUNT];
|
||||
/* +0x648 */ MPTR ukn648Callback[Espresso::CORE_COUNT];
|
||||
/* +0x654 */ MPTR ukn654Callback[Espresso::CORE_COUNT];
|
||||
|
||||
/* +0x660 */ uint32 ukn660;
|
||||
|
||||
// TLS
|
||||
/* +0x664 */ uint16 numAllocatedTLSBlocks;
|
||||
/* +0x666 */ sint16 tlsStatus;
|
||||
/* +0x668 */ MPTR tlsBlocksMPTR;
|
||||
|
||||
/* +0x66C */ MEMPTR<coreinit::OSFastMutex> waitingForFastMutex;
|
||||
/* +0x670 */ coreinit::OSFastMutexLink contendedFastMutex; // link or queue?
|
||||
/* +0x678 */ coreinit::OSFastMutexLink ownedFastMutex; // link or queue?
|
||||
|
||||
/* +0x680 */ uint32 padding680[28 / 4];
|
||||
};
|
||||
|
||||
static_assert(sizeof(OSThread_t) == 0x6A0-4); // todo - determine correct size
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
void InitializeThread();
|
||||
void InitializeConcurrency();
|
||||
|
||||
OSThread_t* OSGetDefaultThread(sint32 coreIndex);
|
||||
void* OSGetDefaultThreadStack(sint32 coreIndex, uint32& size);
|
||||
|
||||
bool OSCreateThreadType(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop2, sint32 stackSize, sint32 priority, uint32 attr, OSThread_t::THREAD_TYPE threadType);
|
||||
void OSCreateThreadInternal(OSThread_t* thread, uint32 entryPoint, MPTR stackLowerBaseAddr, uint32 stackSize, uint8 affinityMask, OSThread_t::THREAD_TYPE threadType);
|
||||
bool OSRunThread(OSThread_t* thread, MPTR funcAddress, sint32 numParam, void* ptrParam);
|
||||
void OSExitThread(sint32 exitValue);
|
||||
void OSDetachThread(OSThread_t* thread);
|
||||
|
||||
OSThread_t* OSGetCurrentThread();
|
||||
void OSSetCurrentThread(uint32 coreIndex, OSThread_t* thread);
|
||||
|
||||
void __OSSetThreadBasePriority(OSThread_t* thread, sint32 newPriority);
|
||||
void __OSUpdateThreadEffectivePriority(OSThread_t* thread);
|
||||
|
||||
bool OSSetThreadPriority(OSThread_t* thread, sint32 newPriority);
|
||||
uint32 OSGetThreadAffinity(OSThread_t* thread);
|
||||
|
||||
void OSSetThreadName(OSThread_t* thread, char* name);
|
||||
char* OSGetThreadName(OSThread_t* thread);
|
||||
|
||||
sint32 __OSResumeThreadInternal(OSThread_t* thread, sint32 resumeCount);
|
||||
sint32 OSResumeThread(OSThread_t* thread);
|
||||
void OSContinueThread(OSThread_t* thread);
|
||||
void __OSSuspendThreadInternal(OSThread_t* thread);
|
||||
void OSSuspendThread(OSThread_t* thread);
|
||||
void OSSleepThread(OSThreadQueue* threadQueue);
|
||||
void OSWakeupThread(OSThreadQueue* threadQueue);
|
||||
|
||||
void OSTestThreadCancelInternal();
|
||||
|
||||
void OSYieldThread();
|
||||
void OSSleepTicks(uint64 ticks);
|
||||
|
||||
bool OSIsThreadTerminated(OSThread_t* thread);
|
||||
bool OSIsThreadSuspended(OSThread_t* thread);
|
||||
|
||||
// OSThreadQueue
|
||||
void OSInitThreadQueue(OSThreadQueue* threadQueue);
|
||||
void OSInitThreadQueueEx(OSThreadQueue* threadQueue, void* userData);
|
||||
|
||||
// OSEvent
|
||||
void OSInitEvent(OSEvent* event, OSEvent::EVENT_STATE initialState, OSEvent::EVENT_MODE mode);
|
||||
void OSInitEventEx(OSEvent* event, OSEvent::EVENT_STATE initialState, OSEvent::EVENT_MODE mode, void* userData);
|
||||
void OSResetEvent(OSEvent* event);
|
||||
void OSWaitEventInternal(OSEvent* event); // assumes lock is already held
|
||||
void OSWaitEvent(OSEvent* event);
|
||||
bool OSWaitEventWithTimeout(OSEvent* event, uint64 timeout);
|
||||
void OSSignalEventInternal(OSEvent* event); // assumes lock is already held
|
||||
void OSSignalEvent(OSEvent* event);
|
||||
void OSSignalEventAllInternal(OSEvent* event); // assumes lock is already held
|
||||
void OSSignalEventAll(OSEvent* event);
|
||||
|
||||
// OSRendezvous
|
||||
void OSInitRendezvous(OSRendezvous* rendezvous);
|
||||
bool OSWaitRendezvous(OSRendezvous* rendezvous, uint32 coreMask);
|
||||
|
||||
// OSMutex
|
||||
void OSInitMutex(OSMutex* mutex);
|
||||
void OSInitMutexEx(OSMutex* mutex, void* userData);
|
||||
void OSLockMutex(OSMutex* mutex);
|
||||
bool OSTryLockMutex(OSMutex* mutex);
|
||||
void OSUnlockMutex(OSMutex* mutex);
|
||||
void OSUnlockMutexInternal(OSMutex* mutex);
|
||||
|
||||
// OSCond
|
||||
void OSInitCond(OSCond* cond);
|
||||
void OSInitCondEx(OSCond* cond, void* userData);
|
||||
void OSSignalCond(OSCond* cond);
|
||||
void OSWaitCond(OSCond* cond, OSMutex* mutex);
|
||||
|
||||
// OSSemaphore
|
||||
void OSInitSemaphore(OSSemaphore* semaphore, sint32 initialCount);
|
||||
void OSInitSemaphoreEx(OSSemaphore* semaphore, sint32 initialCount, void* userData);
|
||||
sint32 OSWaitSemaphoreInternal(OSSemaphore* semaphore);
|
||||
sint32 OSWaitSemaphore(OSSemaphore* semaphore);
|
||||
sint32 OSTryWaitSemaphore(OSSemaphore* semaphore);
|
||||
sint32 OSSignalSemaphore(OSSemaphore* semaphore);
|
||||
sint32 OSSignalSemaphoreInternal(OSSemaphore* semaphore, bool reschedule);
|
||||
sint32 OSGetSemaphoreCount(OSSemaphore* semaphore);
|
||||
|
||||
// OSFastMutex
|
||||
void OSFastMutex_Init(OSFastMutex* fastMutex, void* userData);
|
||||
void OSFastMutex_Lock(OSFastMutex* fastMutex);
|
||||
bool OSFastMutex_TryLock(OSFastMutex* fastMutex);
|
||||
void OSFastMutex_Unlock(OSFastMutex* fastMutex);
|
||||
|
||||
// OSFastCond
|
||||
void OSFastCond_Init(OSFastCond* fastCond, void* userData);
|
||||
void OSFastCond_Wait(OSFastCond* fastCond, OSFastMutex* fastMutex);
|
||||
void OSFastCond_Signal(OSFastCond* fastCond);
|
||||
|
||||
// scheduler
|
||||
void OSSchedulerBegin(sint32 numCPUEmulationThreads);
|
||||
void OSSchedulerEnd();
|
||||
|
||||
// internal
|
||||
void __OSAddReadyThreadToRunQueue(OSThread_t* thread);
|
||||
bool __OSCoreShouldSwitchToThread(OSThread_t* currentThread, OSThread_t* newThread);
|
||||
void __OSQueueThreadDeallocation(OSThread_t* thread);
|
||||
}
|
||||
|
||||
#pragma pack()
|
||||
|
||||
// deprecated / clean up required
|
||||
void coreinit_suspendThread(OSThread_t* OSThreadBE, sint32 count = 1);
|
||||
void coreinit_resumeThread(OSThread_t* OSThreadBE, sint32 count = 1);
|
||||
|
||||
MPTR coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_t* hCPU);
|
||||
OSThread_t* coreinitThread_getCurrentThreadDepr(PPCInterpreter_t* hCPU);
|
||||
|
||||
extern MPTR activeThread[256];
|
||||
extern sint32 activeThreadCount;
|
||||
|
||||
extern SlimRWLock srwlock_activeThreadList;
|
||||
169
src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp
Normal file
169
src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
// puts the thread on the waiting queue and changes state to WAITING
|
||||
// relinquishes timeslice
|
||||
// always uses thread->waitQueueLink
|
||||
void OSThreadQueueInternal::queueAndWait(OSThread_t* thread)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
cemu_assert_debug(thread->waitQueueLink.next == nullptr && thread->waitQueueLink.prev == nullptr);
|
||||
thread->currentWaitQueue = this;
|
||||
this->addThreadByPriority(thread, &thread->waitQueueLink);
|
||||
cemu_assert_debug(thread->state == OSThread_t::THREAD_STATE::STATE_RUNNING);
|
||||
thread->state = OSThread_t::THREAD_STATE::STATE_WAITING;
|
||||
PPCCore_switchToSchedulerWithLock();
|
||||
cemu_assert_debug(thread->state == OSThread_t::THREAD_STATE::STATE_RUNNING);
|
||||
}
|
||||
|
||||
void OSThreadQueueInternal::queueOnly(OSThread_t* thread)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
cemu_assert_debug(thread->waitQueueLink.next == nullptr && thread->waitQueueLink.prev == nullptr);
|
||||
thread->currentWaitQueue = this;
|
||||
this->addThreadByPriority(thread, &thread->waitQueueLink);
|
||||
cemu_assert_debug(thread->state == OSThread_t::THREAD_STATE::STATE_RUNNING);
|
||||
thread->state = OSThread_t::THREAD_STATE::STATE_WAITING;
|
||||
}
|
||||
|
||||
// remove thread from wait queue and wake it up
|
||||
void OSThreadQueueInternal::cancelWait(OSThread_t* thread)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
this->removeThread(thread, &thread->waitQueueLink);
|
||||
thread->state = OSThread_t::THREAD_STATE::STATE_READY;
|
||||
thread->currentWaitQueue = nullptr;
|
||||
coreinit::__OSAddReadyThreadToRunQueue(thread);
|
||||
// todo - if waking up a thread on the same core with higher priority, reschedule
|
||||
}
|
||||
|
||||
void OSThreadQueueInternal::addThread(OSThread_t* thread, OSThreadLink* threadLink)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
size_t linkOffset = getLinkOffset(thread, threadLink);
|
||||
// insert after tail
|
||||
if (tail.IsNull())
|
||||
{
|
||||
threadLink->next = nullptr;
|
||||
threadLink->prev = nullptr;
|
||||
head = thread;
|
||||
tail = thread;
|
||||
}
|
||||
else
|
||||
{
|
||||
threadLink->next = nullptr;
|
||||
threadLink->prev = tail;
|
||||
_getThreadLink(tail.GetPtr(), linkOffset)->next = thread;
|
||||
tail = thread;
|
||||
}
|
||||
_debugCheckChain(thread, threadLink);
|
||||
}
|
||||
|
||||
void OSThreadQueueInternal::addThreadByPriority(OSThread_t* thread, OSThreadLink* threadLink)
|
||||
{
|
||||
cemu_assert_debug(tail.IsNull() == head.IsNull()); // either must be set or none at all
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
size_t linkOffset = getLinkOffset(thread, threadLink);
|
||||
if (tail.IsNull())
|
||||
{
|
||||
threadLink->next = nullptr;
|
||||
threadLink->prev = nullptr;
|
||||
head = thread;
|
||||
tail = thread;
|
||||
}
|
||||
else
|
||||
{
|
||||
// insert towards tail based on priority
|
||||
OSThread_t* threadItr = tail.GetPtr();
|
||||
while (threadItr && threadItr->effectivePriority > thread->effectivePriority)
|
||||
threadItr = _getThreadLink(threadItr, linkOffset)->prev.GetPtr();
|
||||
if (threadItr == nullptr)
|
||||
{
|
||||
// insert in front
|
||||
threadLink->next = head;
|
||||
threadLink->prev = nullptr;
|
||||
_getThreadLink(head.GetPtr(), linkOffset)->prev = thread;
|
||||
head = thread;
|
||||
}
|
||||
else
|
||||
{
|
||||
threadLink->prev = threadItr;
|
||||
threadLink->next = _getThreadLink(threadItr, linkOffset)->next;
|
||||
if (_getThreadLink(threadItr, linkOffset)->next)
|
||||
{
|
||||
OSThread_t* threadAfterItr = _getThreadLink(threadItr, linkOffset)->next.GetPtr();
|
||||
_getThreadLink(threadAfterItr, linkOffset)->prev = thread;
|
||||
_getThreadLink(threadItr, linkOffset)->next = thread;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail = thread;
|
||||
_getThreadLink(threadItr, linkOffset)->next = thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
_debugCheckChain(thread, threadLink);
|
||||
}
|
||||
|
||||
void OSThreadQueueInternal::removeThread(OSThread_t* thread, OSThreadLink* threadLink)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
size_t linkOffset = getLinkOffset(thread, threadLink);
|
||||
_debugCheckChain(thread, threadLink);
|
||||
if (threadLink->prev)
|
||||
_getThreadLink(threadLink->prev.GetPtr(), linkOffset)->next = threadLink->next;
|
||||
else
|
||||
head = threadLink->next;
|
||||
if (threadLink->next)
|
||||
_getThreadLink(threadLink->next.GetPtr(), linkOffset)->prev = threadLink->prev;
|
||||
else
|
||||
tail = threadLink->prev;
|
||||
|
||||
threadLink->next = nullptr;
|
||||
threadLink->prev = nullptr;
|
||||
}
|
||||
|
||||
// counterpart for queueAndWait
|
||||
// if reschedule is true then scheduler will switch to woken up thread (if it is runnable on the same core)
|
||||
void OSThreadQueueInternal::wakeupEntireWaitQueue(bool reschedule)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
bool shouldReschedule = false;
|
||||
while (OSThread_t* thread = takeFirstFromQueue(offsetof(OSThread_t, waitQueueLink)))
|
||||
{
|
||||
cemu_assert_debug(thread->state == OSThread_t::THREAD_STATE::STATE_WAITING);
|
||||
cemu_assert_debug(thread->suspendCounter == 0);
|
||||
thread->state = OSThread_t::THREAD_STATE::STATE_READY;
|
||||
thread->currentWaitQueue = nullptr;
|
||||
coreinit::__OSAddReadyThreadToRunQueue(thread);
|
||||
if (reschedule && thread->suspendCounter == 0 && ppcInterpreterCurrentInstance && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread))
|
||||
shouldReschedule = true;
|
||||
}
|
||||
if (shouldReschedule)
|
||||
PPCCore_switchToSchedulerWithLock();
|
||||
}
|
||||
|
||||
// counterpart for queueAndWait
|
||||
// if reschedule is true then scheduler will switch to woken up thread (if it is runnable on the same core)
|
||||
void OSThreadQueueInternal::wakeupSingleThreadWaitQueue(bool reschedule)
|
||||
{
|
||||
cemu_assert_debug(__OSHasSchedulerLock());
|
||||
OSThread_t* thread = takeFirstFromQueue(offsetof(OSThread_t, waitQueueLink));
|
||||
cemu_assert_debug(thread);
|
||||
bool shouldReschedule = false;
|
||||
if (thread)
|
||||
{
|
||||
thread->state = OSThread_t::THREAD_STATE::STATE_READY;
|
||||
thread->currentWaitQueue = nullptr;
|
||||
coreinit::__OSAddReadyThreadToRunQueue(thread);
|
||||
if (reschedule && thread->suspendCounter == 0 && ppcInterpreterCurrentInstance && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread))
|
||||
shouldReschedule = true;
|
||||
}
|
||||
if (shouldReschedule)
|
||||
PPCCore_switchToSchedulerWithLock();
|
||||
}
|
||||
|
||||
}
|
||||
379
src/Cafe/OS/libs/coreinit/coreinit_Time.cpp
Normal file
379
src/Cafe/OS/libs/coreinit/coreinit_Time.cpp
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
uint64 coreinit_getTimerTick()
|
||||
{
|
||||
// bus clock is 1/5th of core clock
|
||||
// timer clock is 1/4th of bus clock
|
||||
return PPCInterpreter_getMainCoreCycleCounter() / 20ULL;
|
||||
}
|
||||
|
||||
uint64 coreinit_getOSTime()
|
||||
{
|
||||
return coreinit_getTimerTick() + ppcCyclesSince2000TimerClock;
|
||||
}
|
||||
|
||||
void export_OSGetTick(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint64 osTime = coreinit_getOSTime();
|
||||
osLib_returnFromFunction(hCPU, (uint32)osTime);
|
||||
}
|
||||
|
||||
void export_OSGetTime(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint64 osTime = coreinit_getOSTime();
|
||||
osLib_returnFromFunction64(hCPU, osTime);
|
||||
}
|
||||
|
||||
__declspec(noinline) uint64 coreinit_getTimeBase_dummy()
|
||||
{
|
||||
return __rdtsc();
|
||||
}
|
||||
|
||||
void export_OSGetSystemTimeDummy(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
osLib_returnFromFunction64(hCPU, coreinit_getTimeBase_dummy());
|
||||
}
|
||||
|
||||
void export_OSGetSystemTime(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
osLib_returnFromFunction64(hCPU, coreinit_getTimerTick());
|
||||
}
|
||||
|
||||
uint32 getLeapDaysUntilYear(uint32 year)
|
||||
{
|
||||
if (year == 0)
|
||||
return 0;
|
||||
return (year + 3) / 4 - (year - 1) / 100 + (year - 1) / 400;
|
||||
}
|
||||
|
||||
bool IsLeapYear(uint32 year)
|
||||
{
|
||||
if (((year & 3) == 0) && (year % 100) != 0)
|
||||
return true;
|
||||
if ((year % 400) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 dayToMonth[12] =
|
||||
{
|
||||
0,31,59,90,120,151,181,212,243,273,304,334
|
||||
};
|
||||
|
||||
uint32 dayToMonthLeapYear[12] =
|
||||
{
|
||||
0,31,60,91,121,152,182,213,244,274,305,335
|
||||
};
|
||||
|
||||
uint32 getDayInYearByYearAndMonth(uint32 year, uint32 month)
|
||||
{
|
||||
// Project Zero Maiden of Black Water (JPN) gives us an invalid calendar object
|
||||
month %= 12; // or return 0 if too big?
|
||||
|
||||
if (IsLeapYear(year))
|
||||
return dayToMonthLeapYear[month];
|
||||
|
||||
return dayToMonth[month];
|
||||
}
|
||||
|
||||
|
||||
inline const uint64 DAY_BIAS_2000 = 0xB2575;
|
||||
|
||||
uint64 OSCalendarTimeToTicks(OSCalendarTime_t *calendar)
|
||||
{
|
||||
uint32 year = calendar->year;
|
||||
|
||||
uint32 leapDays = getLeapDaysUntilYear(year);
|
||||
uint32 startDayOfCurrentMonth = getDayInYearByYearAndMonth(year, calendar->month);
|
||||
|
||||
uint64 dayInYear = (startDayOfCurrentMonth + calendar->dayOfMonth) - 1;
|
||||
|
||||
uint64 dayCount = dayInYear + year * 365 + leapDays - DAY_BIAS_2000;
|
||||
|
||||
// convert date to seconds
|
||||
uint64 tSeconds = 0;
|
||||
tSeconds += (uint64)dayCount * 24 * 60 * 60;
|
||||
tSeconds += (uint64)calendar->hour * 60 * 60;
|
||||
tSeconds += (uint64)calendar->minute * 60;
|
||||
tSeconds += (uint64)calendar->second;
|
||||
|
||||
uint64 tSubSecondTicks = 0;
|
||||
tSubSecondTicks += (uint64)calendar->millisecond * ESPRESSO_TIMER_CLOCK / 1000;
|
||||
tSubSecondTicks += (uint64)calendar->microsecond * ESPRESSO_TIMER_CLOCK / 1000000;
|
||||
|
||||
return tSeconds * ESPRESSO_TIMER_CLOCK + tSubSecondTicks;
|
||||
}
|
||||
|
||||
void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct)
|
||||
{
|
||||
uint64 tSubSecondTicks = ticks % ESPRESSO_TIMER_CLOCK;
|
||||
uint64 tSeconds = ticks / ESPRESSO_TIMER_CLOCK;
|
||||
|
||||
uint64 microsecond = tSubSecondTicks * 1000000ull / ESPRESSO_TIMER_CLOCK;
|
||||
microsecond %= 1000ull;
|
||||
calenderStruct->microsecond = (uint32)microsecond;
|
||||
|
||||
uint64 millisecond = tSubSecondTicks * 1000ull / ESPRESSO_TIMER_CLOCK;
|
||||
millisecond %= 1000ull;
|
||||
calenderStruct->millisecond = (uint32)millisecond;
|
||||
|
||||
uint64 dayOfWeek = (tSeconds/(24ull * 60 * 60) + 6ull) % 7ull;
|
||||
uint64 secondOfDay = (tSeconds % (24ull * 60 * 60));
|
||||
|
||||
calenderStruct->dayOfWeek = (sint32)dayOfWeek;
|
||||
|
||||
uint64 daysSince0AD = tSeconds / (24ull * 60 * 60) + DAY_BIAS_2000;
|
||||
uint32 year = (uint32)(daysSince0AD / 365ull);
|
||||
uint64 yearStartDay = year * 365 + getLeapDaysUntilYear(year);
|
||||
while (yearStartDay > daysSince0AD)
|
||||
{
|
||||
year--;
|
||||
if (IsLeapYear(year))
|
||||
yearStartDay -= 366;
|
||||
else
|
||||
yearStartDay -= 365;
|
||||
}
|
||||
|
||||
calenderStruct->year = year;
|
||||
|
||||
// calculate time of day
|
||||
uint32 tempSecond = (uint32)secondOfDay;
|
||||
calenderStruct->second = tempSecond % 60;
|
||||
tempSecond /= 60;
|
||||
calenderStruct->minute = tempSecond % 60;
|
||||
tempSecond /= 60;
|
||||
calenderStruct->hour = tempSecond % 24;
|
||||
tempSecond /= 24;
|
||||
|
||||
// calculate month and day
|
||||
uint32 dayInYear = (uint32)(daysSince0AD - yearStartDay);
|
||||
bool isLeapYear = IsLeapYear(year);
|
||||
|
||||
uint32 month = 0; // 0-11
|
||||
uint32 dayInMonth = 0;
|
||||
|
||||
if (isLeapYear && dayInYear < (31+29))
|
||||
{
|
||||
if (dayInYear < 31)
|
||||
{
|
||||
// January
|
||||
month = 0;
|
||||
dayInMonth = dayInYear;
|
||||
}
|
||||
else
|
||||
{
|
||||
// February
|
||||
month = 1;
|
||||
dayInMonth = dayInYear-31;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isLeapYear)
|
||||
dayInYear--;
|
||||
|
||||
dayInMonth = dayInYear;
|
||||
if (dayInYear >= 334)
|
||||
{
|
||||
// December
|
||||
month = 11;
|
||||
dayInMonth -= 334;
|
||||
}
|
||||
else if (dayInYear >= 304)
|
||||
{
|
||||
// November
|
||||
month = 10;
|
||||
dayInMonth -= 304;
|
||||
}
|
||||
else if (dayInYear >= 273)
|
||||
{
|
||||
// October
|
||||
month = 9;
|
||||
dayInMonth -= 273;
|
||||
}
|
||||
else if (dayInYear >= 243)
|
||||
{
|
||||
// September
|
||||
month = 8;
|
||||
dayInMonth -= 243;
|
||||
}
|
||||
else if (dayInYear >= 212)
|
||||
{
|
||||
// August
|
||||
month = 7;
|
||||
dayInMonth -= 212;
|
||||
}
|
||||
else if (dayInYear >= 181)
|
||||
{
|
||||
// July
|
||||
month = 6;
|
||||
dayInMonth -= 181;
|
||||
}
|
||||
else if (dayInYear >= 151)
|
||||
{
|
||||
// June
|
||||
month = 5;
|
||||
dayInMonth -= 151;
|
||||
}
|
||||
else if (dayInYear >= 120)
|
||||
{
|
||||
// May
|
||||
month = 4;
|
||||
dayInMonth -= 120;
|
||||
}
|
||||
else if (dayInYear >= 90)
|
||||
{
|
||||
// April
|
||||
month = 3;
|
||||
dayInMonth -= 90;
|
||||
}
|
||||
else if (dayInYear >= 59)
|
||||
{
|
||||
// March
|
||||
month = 2;
|
||||
dayInMonth -= 59;
|
||||
}
|
||||
else if (dayInYear >= 31)
|
||||
{
|
||||
// February
|
||||
month = 1;
|
||||
dayInMonth -= 31;
|
||||
}
|
||||
else
|
||||
{
|
||||
// January
|
||||
month = 0;
|
||||
dayInMonth -= 0;
|
||||
}
|
||||
}
|
||||
|
||||
calenderStruct->dayOfYear = dayInYear;
|
||||
calenderStruct->month = month;
|
||||
calenderStruct->dayOfMonth = dayInMonth + 1;
|
||||
}
|
||||
|
||||
uint32 getDaysInMonth(uint32 year, uint32 month)
|
||||
{
|
||||
switch (month)
|
||||
{
|
||||
case 0: // January
|
||||
return 31;
|
||||
case 1: // February
|
||||
return IsLeapYear(year) ? 29 : 28;
|
||||
case 2: // March
|
||||
return 31;
|
||||
case 3: // April
|
||||
return 30;
|
||||
case 4: // May
|
||||
return 31;
|
||||
case 5: // June
|
||||
return 30;
|
||||
case 6: // July
|
||||
return 31;
|
||||
case 7: // August
|
||||
return 31;
|
||||
case 8: // September
|
||||
return 30;
|
||||
case 9: // October
|
||||
return 31;
|
||||
case 10: // November
|
||||
return 30;
|
||||
case 11: // December
|
||||
return 31;
|
||||
default:
|
||||
cemu_assert(false);
|
||||
}
|
||||
return 31;
|
||||
}
|
||||
|
||||
void verifyDateMatch(OSCalendarTime_t* ct1, OSCalendarTime_t* ct2)
|
||||
{
|
||||
sint64 m1 = ct1->millisecond * 1000 + ct1->microsecond;
|
||||
sint64 m2 = ct2->millisecond * 1000 + ct2->microsecond;
|
||||
sint64 microDif = std::abs(m1 - m2);
|
||||
|
||||
if (ct1->year != ct2->year ||
|
||||
ct1->month != ct2->month ||
|
||||
ct1->dayOfMonth != ct2->dayOfMonth ||
|
||||
ct1->hour != ct2->hour ||
|
||||
ct1->minute != ct2->minute ||
|
||||
ct1->second != ct2->second ||
|
||||
microDif > 1ull)
|
||||
{
|
||||
debug_printf("Mismatch\n");
|
||||
assert_dbg();
|
||||
}
|
||||
}
|
||||
|
||||
void timeTest()
|
||||
{
|
||||
sint32 iterCount = 0;
|
||||
|
||||
OSCalendarTime_t ct{};
|
||||
ct.year = 2000;
|
||||
ct.month = 1;
|
||||
ct.dayOfMonth = 1;
|
||||
ct.hour = 1;
|
||||
ct.minute = 1;
|
||||
ct.second = 1;
|
||||
ct.millisecond = 123;
|
||||
ct.microsecond = 321;
|
||||
|
||||
while (true)
|
||||
{
|
||||
iterCount++;
|
||||
|
||||
|
||||
uint64 ticks = OSCalendarTimeToTicks(&ct);
|
||||
|
||||
// make sure converting it back results in the same date
|
||||
OSCalendarTime_t ctTemp;
|
||||
OSTicksToCalendarTime(ticks, &ctTemp);
|
||||
verifyDateMatch(&ct, &ctTemp);
|
||||
|
||||
// add a day
|
||||
ticks += 24ull * 60 * 60 * ESPRESSO_TIMER_CLOCK;
|
||||
|
||||
OSCalendarTime_t ctOutput;
|
||||
OSTicksToCalendarTime(ticks, &ctOutput);
|
||||
|
||||
OSCalendarTime_t ctExpected;
|
||||
ctExpected = ct;
|
||||
// add a day manually
|
||||
sint32 daysInMonth = getDaysInMonth(ctExpected.year, ctExpected.month);
|
||||
ctExpected.dayOfMonth = ctExpected.dayOfMonth + 1;
|
||||
if (ctExpected.dayOfMonth >= daysInMonth+1)
|
||||
{
|
||||
ctExpected.dayOfMonth = 1;
|
||||
ctExpected.month = ctExpected.month + 1;
|
||||
if (ctExpected.month > 11)
|
||||
{
|
||||
ctExpected.month = 0;
|
||||
ctExpected.year = ctExpected.year + 1;
|
||||
}
|
||||
}
|
||||
|
||||
verifyDateMatch(&ctExpected, &ctOutput);
|
||||
|
||||
ct = ctOutput;
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeTimeAndCalendar()
|
||||
{
|
||||
osLib_addFunction("coreinit", "OSGetTime", export_OSGetTime);
|
||||
osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTimeDummy); // register dummy HLE function to get Cemuhook to patch our dummy instead of the real function
|
||||
osLib_addFunction("coreinit", "OSGetTick", export_OSGetTick);
|
||||
|
||||
cafeExportRegister("coreinit", OSTicksToCalendarTime, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSCalendarTimeToTicks, LogType::Placeholder);
|
||||
|
||||
osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTime);
|
||||
|
||||
//timeTest();
|
||||
}
|
||||
};
|
||||
58
src/Cafe/OS/libs/coreinit/coreinit_Time.h
Normal file
58
src/Cafe/OS/libs/coreinit/coreinit_Time.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
#include "Cafe/HW/Espresso/Const.h"
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ sint32be second; // 0-59
|
||||
/* +0x04 */ sint32be minute; // 0-59
|
||||
/* +0x08 */ sint32be hour; // 0-23
|
||||
/* +0x0C */ sint32be dayOfMonth; // 1-31
|
||||
/* +0x10 */ sint32be month; // 0-11
|
||||
/* +0x14 */ sint32be year; // 2000-...
|
||||
/* +0x18 */ sint32be dayOfWeek; // 0-6
|
||||
/* +0x1C */ sint32be dayOfYear; // 0-365
|
||||
/* +0x20 */ sint32be millisecond; // 0-999
|
||||
/* +0x24 */ sint32be microsecond; // 0-999
|
||||
}OSCalendarTime_t;
|
||||
|
||||
static_assert(sizeof(OSCalendarTime_t) == 0x28);
|
||||
|
||||
namespace EspressoTime
|
||||
{
|
||||
typedef sint64 TimerTicks;
|
||||
|
||||
constexpr sint64 GetCoreClock()
|
||||
{
|
||||
return Espresso::CORE_CLOCK;
|
||||
}
|
||||
|
||||
constexpr sint64 GetBusClock()
|
||||
{
|
||||
return Espresso::BUS_CLOCK;
|
||||
}
|
||||
|
||||
constexpr sint64 GetTimerClock()
|
||||
{
|
||||
return Espresso::TIMER_CLOCK;
|
||||
}
|
||||
|
||||
inline TimerTicks ConvertNsToTimerTicks(uint64 ns)
|
||||
{
|
||||
return ((GetTimerClock() / 31250LL) * ((ns)) / 32000LL);
|
||||
}
|
||||
};
|
||||
|
||||
void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct);
|
||||
|
||||
uint64 coreinit_getOSTime();
|
||||
uint64 coreinit_getTimerTick();
|
||||
|
||||
static uint64 OSGetSystemTime()
|
||||
{
|
||||
return coreinit_getTimerTick();
|
||||
}
|
||||
|
||||
void InitializeTimeAndCalendar();
|
||||
};
|
||||
119
src/Cafe/OS/libs/dmae/dmae.cpp
Normal file
119
src/Cafe/OS/libs/dmae/dmae.cpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
|
||||
|
||||
#define DMAE_ENDIAN_NONE 0
|
||||
#define DMAE_ENDIAN_16 1
|
||||
#define DMAE_ENDIAN_32 2
|
||||
#define DMAE_ENDIAN_64 3
|
||||
|
||||
uint64 dmaeRetiredTimestamp = 0;
|
||||
|
||||
uint64 dmae_getTimestamp()
|
||||
{
|
||||
return coreinit::coreinit_getTimerTick();
|
||||
}
|
||||
|
||||
void dmae_setRetiredTimestamp(uint64 timestamp)
|
||||
{
|
||||
dmaeRetiredTimestamp = timestamp;
|
||||
}
|
||||
|
||||
void dmaeExport_DMAECopyMem(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
if( hCPU->gpr[6] == DMAE_ENDIAN_NONE )
|
||||
{
|
||||
// don't change endianness
|
||||
memcpy(memory_getPointerFromVirtualOffset(hCPU->gpr[3]), memory_getPointerFromVirtualOffset(hCPU->gpr[4]), hCPU->gpr[5]*4);
|
||||
}
|
||||
else if( hCPU->gpr[6] == DMAE_ENDIAN_32 )
|
||||
{
|
||||
// swap per uint32
|
||||
uint32* srcBuffer = (uint32*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
|
||||
uint32* dstBuffer = (uint32*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
for(uint32 i=0; i<hCPU->gpr[5]; i++)
|
||||
{
|
||||
dstBuffer[i] = _swapEndianU32(srcBuffer[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "DMAECopyMem(): Unsupported endian swap\n");
|
||||
}
|
||||
uint64 dmaeTimestamp = dmae_getTimestamp();
|
||||
dmae_setRetiredTimestamp(dmaeTimestamp);
|
||||
if(hCPU->gpr[5] > 0)
|
||||
LatteBufferCache_notifyDCFlush(hCPU->gpr[3], hCPU->gpr[5]*4);
|
||||
osLib_returnFromFunction64(hCPU, dmaeTimestamp);
|
||||
}
|
||||
|
||||
void dmaeExport_DMAEFillMem(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint32* dstBuffer = (uint32*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
uint32 value = hCPU->gpr[4];
|
||||
uint32 numU32s = hCPU->gpr[5];
|
||||
value = _swapEndianU32(value);
|
||||
for(uint32 i=0; i<numU32s; i++)
|
||||
{
|
||||
*dstBuffer = value;
|
||||
dstBuffer++;
|
||||
}
|
||||
uint64 dmaeTimestamp = dmae_getTimestamp();
|
||||
dmae_setRetiredTimestamp(dmaeTimestamp);
|
||||
osLib_returnFromFunction64(hCPU, dmaeTimestamp);
|
||||
}
|
||||
|
||||
void dmaeExport_DMAEWaitDone(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
//debug_printf("DMAEWaitDone(...)\n");
|
||||
// parameter:
|
||||
// r3/r4 uint64 dmaeTimestamp
|
||||
osLib_returnFromFunction(hCPU, 1);
|
||||
}
|
||||
|
||||
void dmaeExport_DMAESemaphore(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameter:
|
||||
// r3 MPTR addr
|
||||
// r4 uint32 actionType
|
||||
|
||||
uint32 actionType = hCPU->gpr[4];
|
||||
|
||||
std::atomic<uint64le>* semaphore = _rawPtrToAtomic((uint64le*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]));
|
||||
|
||||
if( actionType == 1 )
|
||||
{
|
||||
// Signal Semaphore
|
||||
semaphore->fetch_add(1);
|
||||
}
|
||||
else if (actionType == 0) // wait
|
||||
{
|
||||
forceLogDebug_printf("DMAESemaphore: Unsupported wait operation");
|
||||
semaphore->fetch_sub(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
forceLogDebug_printf("DMAESemaphore unknown action type %d", actionType);
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
|
||||
uint64 dmaeTimestamp = dmae_getTimestamp();
|
||||
dmae_setRetiredTimestamp(dmaeTimestamp);
|
||||
osLib_returnFromFunction64(hCPU, dmaeTimestamp);
|
||||
}
|
||||
|
||||
void dmaeExport_DMAEGetRetiredTimeStamp(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
debug_printf("DMAEGetRetiredTimeStamp()\n");
|
||||
osLib_returnFromFunction64(hCPU, dmaeRetiredTimestamp);
|
||||
}
|
||||
|
||||
|
||||
void dmae_load()
|
||||
{
|
||||
osLib_addFunction("dmae", "DMAECopyMem", dmaeExport_DMAECopyMem);
|
||||
osLib_addFunction("dmae", "DMAEFillMem", dmaeExport_DMAEFillMem);
|
||||
osLib_addFunction("dmae", "DMAEWaitDone", dmaeExport_DMAEWaitDone);
|
||||
osLib_addFunction("dmae", "DMAESemaphore", dmaeExport_DMAESemaphore);
|
||||
osLib_addFunction("dmae", "DMAEGetRetiredTimeStamp", dmaeExport_DMAEGetRetiredTimeStamp);
|
||||
}
|
||||
1
src/Cafe/OS/libs/dmae/dmae.h
Normal file
1
src/Cafe/OS/libs/dmae/dmae.h
Normal file
|
|
@ -0,0 +1 @@
|
|||
void dmae_load();
|
||||
16
src/Cafe/OS/libs/drmapp/drmapp.cpp
Normal file
16
src/Cafe/OS/libs/drmapp/drmapp.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "drmapp.h"
|
||||
|
||||
namespace drmapp
|
||||
{
|
||||
uint32 NupChkIsFinished(uint32 ukn)
|
||||
{
|
||||
forceLogDebug_printf("drmapp.NupChkIsFinished() - placeholder");
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
cafeExportRegisterFunc(NupChkIsFinished, "drmapp", "NupChkIsFinished__3RplFv", LogType::Placeholder);
|
||||
}
|
||||
}
|
||||
5
src/Cafe/OS/libs/drmapp/drmapp.h
Normal file
5
src/Cafe/OS/libs/drmapp/drmapp.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
namespace drmapp
|
||||
{
|
||||
void Initialize();
|
||||
}
|
||||
396
src/Cafe/OS/libs/erreula/erreula.cpp
Normal file
396
src/Cafe/OS/libs/erreula/erreula.cpp
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "erreula.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
|
||||
#include <imgui.h>
|
||||
#include "imgui/imgui_extension.h"
|
||||
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_FS.h"
|
||||
#include "Cafe/OS/libs/vpad/vpad.h"
|
||||
|
||||
namespace nn
|
||||
{
|
||||
namespace erreula
|
||||
{
|
||||
#define RESULTTYPE_NONE 0
|
||||
#define RESULTTYPE_FINISH 1
|
||||
#define RESULTTYPE_NEXT 2
|
||||
#define RESULTTYPE_JUMP 3
|
||||
#define RESULTTYPE_PASSWORD 4
|
||||
|
||||
#define ERRORTYPE_CODE 0
|
||||
#define ERRORTYPE_TEXT 1
|
||||
#define ERRORTYPE_TEXT_ONE_BUTTON 2
|
||||
#define ERRORTYPE_TEXT_TWO_BUTTON 3
|
||||
|
||||
#define ERREULA_STATE_HIDDEN 0
|
||||
#define ERREULA_STATE_APPEARING 1
|
||||
#define ERREULA_STATE_VISIBLE 2
|
||||
#define ERREULA_STATE_DISAPPEARING 3
|
||||
|
||||
struct AppearArg_t
|
||||
{
|
||||
AppearArg_t() = default;
|
||||
AppearArg_t(const AppearArg_t& o)
|
||||
{
|
||||
errorType = o.errorType;
|
||||
screenType = o.screenType;
|
||||
controllerType = o.controllerType;
|
||||
holdType = o.holdType;
|
||||
errorCode = o.errorCode;
|
||||
framerate = o.framerate;
|
||||
text = o.text;
|
||||
button1Text = o.button1Text;
|
||||
button2Text = o.button2Text;
|
||||
title = o.title;
|
||||
drawCursor = o.drawCursor;
|
||||
}
|
||||
|
||||
uint32be errorType;
|
||||
uint32be screenType;
|
||||
uint32be controllerType;
|
||||
uint32be holdType;
|
||||
uint32be errorCode;
|
||||
uint32be framerate;
|
||||
MEMPTR<uint16be> text;
|
||||
MEMPTR<uint16be> button1Text;
|
||||
MEMPTR<uint16be> button2Text;
|
||||
MEMPTR<uint16be> title;
|
||||
uint8 padding[3];
|
||||
bool drawCursor{};
|
||||
};
|
||||
|
||||
static_assert(sizeof(AppearArg_t) == 0x2C); // maybe larger
|
||||
|
||||
struct HomeNixSignArg_t
|
||||
{
|
||||
uint32be framerate;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HomeNixSignArg_t) == 0x4); // maybe larger
|
||||
|
||||
struct ControllerInfo_t
|
||||
{
|
||||
MEMPTR<VPADStatus_t> vpadStatus;
|
||||
MEMPTR<KPADStatus_t> kpadStatus[4]; // or 7 now like KPAD_MAX_CONTROLLERS?
|
||||
};
|
||||
|
||||
static_assert(sizeof(ControllerInfo_t) == 0x14); // maybe larger
|
||||
|
||||
struct ErrEula_t
|
||||
{
|
||||
coreinit::OSMutex mutex;
|
||||
uint32 regionType;
|
||||
uint32 langType;
|
||||
MEMPTR<coreinit::FSClient_t> fsClient;
|
||||
|
||||
AppearArg_t currentDialog;
|
||||
uint32 state;
|
||||
bool buttonPressed;
|
||||
bool rightButtonPressed;
|
||||
|
||||
bool homeNixSignVisible;
|
||||
|
||||
std::chrono::steady_clock::time_point stateTimer{};
|
||||
} g_errEula = {};
|
||||
|
||||
|
||||
|
||||
std::wstring GetText(uint16be* text)
|
||||
{
|
||||
std::wstringstream result;
|
||||
while(*text != 0)
|
||||
{
|
||||
auto c = (uint16)*text;
|
||||
result << static_cast<wchar_t>(c);
|
||||
text++;
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
|
||||
void export_ErrEulaCreate(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamMEMPTR(thisptr, uint8, 0);
|
||||
ppcDefineParamU32(regionType, 1);
|
||||
ppcDefineParamU32(langType, 2);
|
||||
ppcDefineParamMEMPTR(fsClient, coreinit::FSClient_t, 3);
|
||||
|
||||
coreinit::OSLockMutex(&g_errEula.mutex);
|
||||
|
||||
g_errEula.regionType = regionType;
|
||||
g_errEula.langType = langType;
|
||||
g_errEula.fsClient = fsClient;
|
||||
|
||||
coreinit::OSUnlockMutex(&g_errEula.mutex);
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_AppearHomeNixSign(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
g_errEula.homeNixSignVisible = TRUE;
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_AppearError(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamMEMPTR(arg, AppearArg_t, 0);
|
||||
|
||||
g_errEula.currentDialog = *arg.GetPtr();
|
||||
g_errEula.state = ERREULA_STATE_APPEARING;
|
||||
g_errEula.buttonPressed = false;
|
||||
g_errEula.rightButtonPressed = false;
|
||||
|
||||
g_errEula.stateTimer = tick_cached();
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_GetStateErrorViewer(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
osLib_returnFromFunction(hCPU, g_errEula.state);
|
||||
}
|
||||
void export_DisappearError(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
g_errEula.state = ERREULA_STATE_HIDDEN;
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_ChangeLang(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(langType, 0);
|
||||
g_errEula.langType = langType;
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_IsDecideSelectButtonError(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
if (g_errEula.buttonPressed)
|
||||
forceLogDebug_printf("IsDecideSelectButtonError: TRUE");
|
||||
osLib_returnFromFunction(hCPU, g_errEula.buttonPressed);
|
||||
}
|
||||
|
||||
void export_IsDecideSelectLeftButtonError(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
if (g_errEula.buttonPressed)
|
||||
forceLogDebug_printf("IsDecideSelectLeftButtonError: TRUE");
|
||||
osLib_returnFromFunction(hCPU, g_errEula.buttonPressed);
|
||||
}
|
||||
|
||||
void export_IsDecideSelectRightButtonError(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
if (g_errEula.rightButtonPressed)
|
||||
forceLogDebug_printf("IsDecideSelectRightButtonError: TRUE");
|
||||
osLib_returnFromFunction(hCPU, g_errEula.rightButtonPressed);
|
||||
}
|
||||
|
||||
void export_IsAppearHomeNixSign(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
osLib_returnFromFunction(hCPU, g_errEula.homeNixSignVisible);
|
||||
}
|
||||
|
||||
void export_DisappearHomeNixSign(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
g_errEula.homeNixSignVisible = FALSE;
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void export_GetResultType(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint32 result = RESULTTYPE_NONE;
|
||||
if (g_errEula.buttonPressed || g_errEula.rightButtonPressed)
|
||||
{
|
||||
forceLogDebug_printf("GetResultType: FINISH");
|
||||
result = RESULTTYPE_FINISH;
|
||||
}
|
||||
|
||||
osLib_returnFromFunction(hCPU, result);
|
||||
}
|
||||
|
||||
void export_Calc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamMEMPTR(controllerInfo, ControllerInfo_t, 0);
|
||||
// TODO: check controller buttons bla to accept dialog?
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void render(bool mainWindow)
|
||||
{
|
||||
if(g_errEula.state == ERREULA_STATE_HIDDEN)
|
||||
return;
|
||||
|
||||
if(g_errEula.state == ERREULA_STATE_APPEARING)
|
||||
{
|
||||
if(std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() <= 1000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_errEula.state = ERREULA_STATE_VISIBLE;
|
||||
g_errEula.stateTimer = tick_cached();
|
||||
}
|
||||
/*else if(g_errEula.state == STATE_VISIBLE)
|
||||
{
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() >= 1000)
|
||||
{
|
||||
g_errEula.state = STATE_DISAPPEARING;
|
||||
g_errEula.stateTimer = tick_cached();
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
else if(g_errEula.state == ERREULA_STATE_DISAPPEARING)
|
||||
{
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() >= 2000)
|
||||
{
|
||||
g_errEula.state = ERREULA_STATE_HIDDEN;
|
||||
g_errEula.stateTimer = tick_cached();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const AppearArg_t& appearArg = g_errEula.currentDialog;
|
||||
std::string text;
|
||||
const uint32 errorCode = (uint32)appearArg.errorCode;
|
||||
if (errorCode != 0)
|
||||
{
|
||||
const uint32 errorCodeHigh = errorCode / 10000;
|
||||
const uint32 errorCodeLow = errorCode % 10000;
|
||||
text = fmt::format("Error-Code: {:03}-{:04}\n", errorCodeHigh, errorCodeLow);
|
||||
}
|
||||
|
||||
auto font = ImGui_GetFont(32.0f);
|
||||
if (!font)
|
||||
return;
|
||||
|
||||
const auto kPopupFlags = ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings;
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
ImVec2 position = { io.DisplaySize.x / 2.0f, io.DisplaySize.y / 2.0f };
|
||||
ImVec2 pivot = { 0.5f, 0.5f };
|
||||
ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);
|
||||
ImGui::SetNextWindowBgAlpha(0.9f);
|
||||
ImGui::PushFont(font);
|
||||
|
||||
std::string title = "ErrEula";
|
||||
if (appearArg.title)
|
||||
title = boost::nowide::narrow(GetText(appearArg.title.GetPtr()));
|
||||
|
||||
if (ImGui::Begin(title.c_str(), nullptr, kPopupFlags))
|
||||
{
|
||||
const float startx = ImGui::GetWindowSize().x / 2.0f;
|
||||
|
||||
switch ((uint32)appearArg.errorType)
|
||||
{
|
||||
default:
|
||||
{
|
||||
// TODO layout based on error code
|
||||
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
|
||||
ImGui::Spacing();
|
||||
ImGui::SetCursorPosX(startx - 50);
|
||||
g_errEula.buttonPressed |= ImGui::Button("OK", {100, 0});
|
||||
|
||||
break;
|
||||
}
|
||||
case ERRORTYPE_TEXT:
|
||||
{
|
||||
std::string txtTmp = "Unknown Error";
|
||||
if (appearArg.text)
|
||||
txtTmp = boost::nowide::narrow(GetText(appearArg.text.GetPtr()));
|
||||
|
||||
text += txtTmp;
|
||||
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::SetCursorPosX(startx - 50);
|
||||
g_errEula.buttonPressed |= ImGui::Button("OK", { 100, 0 });
|
||||
break;
|
||||
}
|
||||
case ERRORTYPE_TEXT_ONE_BUTTON:
|
||||
{
|
||||
std::string txtTmp = "Unknown Error";
|
||||
if (appearArg.text)
|
||||
txtTmp = boost::nowide::narrow(GetText(appearArg.text.GetPtr()));
|
||||
|
||||
text += txtTmp;
|
||||
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
|
||||
ImGui::Spacing();
|
||||
|
||||
std::string button1 = "Yes";
|
||||
if (appearArg.button1Text)
|
||||
button1 = boost::nowide::narrow(GetText(appearArg.button1Text.GetPtr()));
|
||||
|
||||
float width = std::max(100.0f, ImGui::CalcTextSize(button1.c_str()).x + 10.0f);
|
||||
ImGui::SetCursorPosX(startx - (width / 2.0f));
|
||||
g_errEula.buttonPressed |= ImGui::Button(button1.c_str(), { width, 0 });
|
||||
break;
|
||||
}
|
||||
case ERRORTYPE_TEXT_TWO_BUTTON:
|
||||
{
|
||||
std::string txtTmp = "Unknown Error";
|
||||
if (appearArg.text)
|
||||
txtTmp = boost::nowide::narrow(GetText(appearArg.text.GetPtr()));
|
||||
|
||||
text += txtTmp;
|
||||
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
|
||||
ImGui::Spacing();
|
||||
|
||||
std::string button1 = "Yes";
|
||||
if (appearArg.button1Text)
|
||||
button1 = boost::nowide::narrow(GetText(appearArg.button1Text.GetPtr()));
|
||||
std::string button2 = "No";
|
||||
if (appearArg.button2Text)
|
||||
button2 = boost::nowide::narrow(GetText(appearArg.button2Text.GetPtr()));
|
||||
|
||||
|
||||
float width1 = std::max(100.0f, ImGui::CalcTextSize(button1.c_str()).x + 10.0f);
|
||||
float width2 = std::max(100.0f, ImGui::CalcTextSize(button2.c_str()).x + 10.0f);
|
||||
ImGui::SetCursorPosX(startx - (width1 / 2.0f) - (width2 / 2.0f) - 10);
|
||||
|
||||
g_errEula.buttonPressed |= ImGui::Button(button1.c_str(), { width1, 0 });
|
||||
ImGui::SameLine();
|
||||
|
||||
g_errEula.rightButtonPressed |= ImGui::Button(button2.c_str(), { width2, 0 });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
if(g_errEula.buttonPressed || g_errEula.rightButtonPressed)
|
||||
{
|
||||
g_errEula.state = ERREULA_STATE_DISAPPEARING;
|
||||
g_errEula.stateTimer = tick_cached();
|
||||
}
|
||||
}
|
||||
|
||||
void load()
|
||||
{
|
||||
OSInitMutexEx(&g_errEula.mutex, nullptr);
|
||||
|
||||
//osLib_addFunction("erreula", "ErrEulaCreate__3RplFPUcQ3_2nn7erreula10", export_ErrEulaCreate); // copy ctor?
|
||||
osLib_addFunction("erreula", "ErrEulaCreate__3RplFPUcQ3_2nn7erreula10RegionTypeQ3_2nn7erreula8LangTypeP8FSClient", export_ErrEulaCreate);
|
||||
osLib_addFunction("erreula", "ErrEulaAppearHomeNixSign__3RplFRCQ3_2nn7erreula14HomeNixSignArg", export_AppearHomeNixSign);
|
||||
osLib_addFunction("erreula", "ErrEulaAppearError__3RplFRCQ3_2nn7erreula9AppearArg", export_AppearError);
|
||||
osLib_addFunction("erreula", "ErrEulaGetStateErrorViewer__3RplFv", export_GetStateErrorViewer);
|
||||
osLib_addFunction("erreula", "ErrEulaChangeLang__3RplFQ3_2nn7erreula8LangType", export_ChangeLang);
|
||||
osLib_addFunction("erreula", "ErrEulaIsDecideSelectButtonError__3RplFv", export_IsDecideSelectButtonError);
|
||||
osLib_addFunction("erreula", "ErrEulaCalc__3RplFRCQ3_2nn7erreula14ControllerInfo", export_Calc);
|
||||
osLib_addFunction("erreula", "ErrEulaIsDecideSelectLeftButtonError__3RplFv", export_IsDecideSelectLeftButtonError);
|
||||
osLib_addFunction("erreula", "ErrEulaIsDecideSelectRightButtonError__3RplFv", export_IsDecideSelectRightButtonError);
|
||||
osLib_addFunction("erreula", "ErrEulaIsAppearHomeNixSign__3RplFv", export_IsAppearHomeNixSign);
|
||||
osLib_addFunction("erreula", "ErrEulaDisappearHomeNixSign__3RplFv", export_DisappearHomeNixSign);
|
||||
osLib_addFunction("erreula", "ErrEulaGetResultType__3RplFv", export_GetResultType);
|
||||
osLib_addFunction("erreula", "ErrEulaDisappearError__3RplFv", export_DisappearError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src/Cafe/OS/libs/erreula/erreula.h
Normal file
12
src/Cafe/OS/libs/erreula/erreula.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
namespace nn
|
||||
{
|
||||
namespace erreula
|
||||
{
|
||||
void render(bool mainWindow);
|
||||
|
||||
void load();
|
||||
|
||||
}
|
||||
}
|
||||
472
src/Cafe/OS/libs/gx2/GX2.cpp
Normal file
472
src/Cafe/OS/libs/gx2/GX2.cpp
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||
#include "Cafe/HW/Espresso/PPCCallback.h"
|
||||
#include "GX2.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
|
||||
#include "Cafe/HW/Latte/Core/LattePM4.h"
|
||||
|
||||
#include "GX2_Command.h"
|
||||
#include "GX2_State.h"
|
||||
#include "GX2_Memory.h"
|
||||
#include "GX2_Event.h"
|
||||
#include "GX2_Shader.h"
|
||||
#include "GX2_Blit.h"
|
||||
#include "GX2_Draw.h"
|
||||
#include "GX2_Query.h"
|
||||
#include "GX2_Misc.h"
|
||||
#include "GX2_Surface.h"
|
||||
#include "GX2_Surface_Copy.h"
|
||||
#include "GX2_Texture.h"
|
||||
|
||||
#define GX2_TV_RENDER_NONE 0
|
||||
#define GX2_TV_RENDER_480 1
|
||||
#define GX2_TV_RENDER_480_WIDE 2
|
||||
#define GX2_TV_RENDER_720 3
|
||||
#define GX2_TV_RENDER_720I 4
|
||||
#define GX2_TV_RENDER_1080 5
|
||||
#define GX2_TV_RENDER_COUNT 6
|
||||
|
||||
struct
|
||||
{
|
||||
sint32 width;
|
||||
sint32 height;
|
||||
}tvScanBufferResolutions[GX2_TV_RENDER_COUNT] = {
|
||||
0,0,
|
||||
640,480,
|
||||
854,480,
|
||||
1280,720,
|
||||
1280,720,
|
||||
1920,1080
|
||||
};
|
||||
|
||||
uint64 lastSwapTime = 0;
|
||||
|
||||
void gx2Export_GX2SwapScanBuffers(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2SwapScanBuffers()");
|
||||
|
||||
bool isPokken = false;
|
||||
|
||||
uint64 titleId = CafeSystem::GetForegroundTitleId();
|
||||
if (titleId == 0x00050000101DF500ull || titleId == 0x00050000101C5800ull || titleId == 0x00050000101DF400ull)
|
||||
isPokken = true;
|
||||
|
||||
if (isPokken)
|
||||
GX2::GX2DrawDone();
|
||||
|
||||
GX2ReserveCmdSpace(5+2);
|
||||
|
||||
uint64 tick64 = PPCInterpreter_getMainCoreCycleCounter() / 20ULL;
|
||||
lastSwapTime = tick64;
|
||||
// count flip request
|
||||
// is this updated via a PM4 MEM_WRITE operation?
|
||||
|
||||
// Orochi Warriors seems to call GX2SwapScanBuffers on arbitrary threads/cores. The PM4 commands should go through to the GPU as long as there is no active display list and no other core is submitting commands simultaneously
|
||||
// right now, we work around this by avoiding the infinite loop below (request counter incremented, but PM4 not sent)
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
if (GX2::sGX2MainCoreIndex == coreIndex)
|
||||
LatteGPUState.sharedArea->flipRequestCountBE = _swapEndianU32(_swapEndianU32(LatteGPUState.sharedArea->flipRequestCountBE) + 1);
|
||||
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_REQUEST_SWAP_BUFFERS, 1));
|
||||
gx2WriteGather_submitU32AsBE(0); // reserved
|
||||
|
||||
// swap frames
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_TRIGGER_SCANBUFFER_SWAP, 1));
|
||||
gx2WriteGather_submitU32AsBE(0); // reserved
|
||||
|
||||
// wait for flip if the CPU is too far ahead
|
||||
// doing it after swap request is how the actual console does it, but that still causes issues in Pokken
|
||||
while ((sint32)(_swapEndianU32(LatteGPUState.sharedArea->flipRequestCountBE) - _swapEndianU32(LatteGPUState.sharedArea->flipExecuteCountBE)) > 5)
|
||||
{
|
||||
GX2::GX2WaitForFlip();
|
||||
}
|
||||
|
||||
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2CopyColorBufferToScanBuffer(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2CopyColorBufferToScanBuffer(0x%08x,%d)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
GX2ReserveCmdSpace(5);
|
||||
|
||||
// todo: proper implementation
|
||||
|
||||
// hack: Avoid running to far ahead of GPU. Normally this would be guaranteed by the circular buffer model, which we currently dont fully emulate
|
||||
if(GX2::GX2WriteGather_getReadWriteDistance() > 32*1024*1024 )
|
||||
{
|
||||
debug_printf("Waiting for GPU to catch up...\n");
|
||||
PPCInterpreter_relinquishTimeslice(); // release current thread
|
||||
return;
|
||||
}
|
||||
GX2ColorBuffer* colorBuffer = (GX2ColorBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER, 9));
|
||||
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(colorBuffer->surface.imagePtr));
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.width);
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.height);
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.pitch);
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.tileMode.value());
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.swizzle);
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(colorBuffer->viewFirstSlice));
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.format.value());
|
||||
gx2WriteGather_submitU32AsBE(hCPU->gpr[4]);
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2WaitForFreeScanBuffer(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// todo: proper implementation
|
||||
debug_printf("GX2WaitForFreeScanBuffer(): Unimplemented\n");
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2GetCurrentScanBuffer(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// todo: proper implementation
|
||||
uint32 scanTarget = hCPU->gpr[3];
|
||||
GX2ColorBuffer* colorBufferBE = (GX2ColorBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
|
||||
memset(colorBufferBE, 0x00, sizeof(GX2ColorBuffer));
|
||||
colorBufferBE->surface.width = 100;
|
||||
colorBufferBE->surface.height = 100;
|
||||
// note: For now we abuse the tiling aperture memory area as framebuffer pointers
|
||||
if( scanTarget == GX2_SCAN_TARGET_TV )
|
||||
{
|
||||
colorBufferBE->surface.imagePtr = MEMORY_TILINGAPERTURE_AREA_ADDR+0x200000;
|
||||
}
|
||||
else if( scanTarget == GX2_SCAN_TARGET_DRC_FIRST )
|
||||
{
|
||||
colorBufferBE->surface.imagePtr = MEMORY_TILINGAPERTURE_AREA_ADDR+0x40000;
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void coreinitExport_GX2GetSystemTVScanMode(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// 1080p = 7
|
||||
osLib_returnFromFunction(hCPU, 7);
|
||||
}
|
||||
|
||||
void coreinitExport_GX2GetSystemTVAspectRatio(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
osLib_returnFromFunction(hCPU, 1); // 16:9
|
||||
}
|
||||
|
||||
void gx2Export_GX2TempGetGPUVersion(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
osLib_returnFromFunction(hCPU, 2);
|
||||
}
|
||||
|
||||
void _GX2InitScanBuffer(GX2ColorBuffer* colorBuffer, sint32 width, sint32 height, Latte::E_GX2SURFFMT format)
|
||||
{
|
||||
colorBuffer->surface.resFlag = GX2_RESFLAG_USAGE_TEXTURE | GX2_RESFLAG_USAGE_COLOR_BUFFER;
|
||||
colorBuffer->surface.width = width;
|
||||
colorBuffer->surface.height = height;
|
||||
colorBuffer->viewFirstSlice = _swapEndianU32(0);
|
||||
colorBuffer->viewNumSlices = _swapEndianU32(1);
|
||||
colorBuffer->viewMip = _swapEndianU32(0);
|
||||
colorBuffer->surface.numLevels = 1;
|
||||
colorBuffer->surface.dim = Latte::E_DIM::DIM_2D;
|
||||
colorBuffer->surface.swizzle = 0;
|
||||
colorBuffer->surface.depth = 1;
|
||||
colorBuffer->surface.tileMode = Latte::E_GX2TILEMODE::TM_LINEAR_GENERAL;
|
||||
colorBuffer->surface.format = format;
|
||||
colorBuffer->surface.mipPtr = MPTR_NULL;
|
||||
colorBuffer->surface.aa = 0;
|
||||
GX2::GX2CalcSurfaceSizeAndAlignment(&colorBuffer->surface);
|
||||
colorBuffer->surface.resFlag = GX2_RESFLAG_USAGE_TEXTURE | GX2_RESFLAG_USAGE_COLOR_BUFFER | GX2_RESFLAG_USAGE_SCAN_BUFFER;
|
||||
}
|
||||
|
||||
void gx2Export_GX2CalcTVSize(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint32 tvRenderMode = hCPU->gpr[3];
|
||||
Latte::E_GX2SURFFMT format = (Latte::E_GX2SURFFMT)hCPU->gpr[4];
|
||||
uint32 bufferingMode = hCPU->gpr[5];
|
||||
uint32 outputSizeMPTR = hCPU->gpr[6];
|
||||
uint32 outputScaleNeededMPTR = hCPU->gpr[7];
|
||||
|
||||
cemu_assert(tvRenderMode < GX2_TV_RENDER_COUNT);
|
||||
|
||||
uint32 width = tvScanBufferResolutions[tvRenderMode].width;
|
||||
uint32 height = tvScanBufferResolutions[tvRenderMode].height;
|
||||
|
||||
GX2ColorBuffer colorBuffer;
|
||||
memset(&colorBuffer, 0, sizeof(GX2ColorBuffer));
|
||||
_GX2InitScanBuffer(&colorBuffer, width, height, format);
|
||||
|
||||
uint32 imageSize = colorBuffer.surface.imageSize;
|
||||
uint32 alignment = colorBuffer.surface.alignment;
|
||||
|
||||
uint32 alignmentPaddingSize = (alignment - (imageSize%alignment)) % alignment;
|
||||
|
||||
uint32 uknMult = 1; // probably for interlaced?
|
||||
if (tvRenderMode == GX2_TV_RENDER_720I)
|
||||
uknMult = 2;
|
||||
|
||||
uint32 adjustedBufferingMode = bufferingMode;
|
||||
if (tvRenderMode < GX2_TV_RENDER_720)
|
||||
adjustedBufferingMode = 4;
|
||||
|
||||
uint32 bufferedImageSize = (imageSize + alignmentPaddingSize) * adjustedBufferingMode;
|
||||
bufferedImageSize = bufferedImageSize * uknMult - alignmentPaddingSize;
|
||||
|
||||
memory_writeU32(outputSizeMPTR, bufferedImageSize);
|
||||
memory_writeU32(outputScaleNeededMPTR, 0); // todo
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2CalcDRCSize(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
|
||||
ppcDefineParamS32(drcMode, 0);
|
||||
ppcDefineParamU32(format, 1);
|
||||
ppcDefineParamU32(bufferingMode, 2);
|
||||
ppcDefineParamMPTR(sizeMPTR, 3);
|
||||
ppcDefineParamMPTR(scaleNeededMPTR, 4);
|
||||
|
||||
uint32 width = 0;
|
||||
uint32 height = 0;
|
||||
if (drcMode > 0)
|
||||
{
|
||||
width = 854;
|
||||
height = 480;
|
||||
}
|
||||
|
||||
GX2ColorBuffer colorBuffer = {};
|
||||
memset(&colorBuffer, 0, sizeof(colorBuffer));
|
||||
_GX2InitScanBuffer(&colorBuffer, width, height, (Latte::E_GX2SURFFMT)format);
|
||||
|
||||
uint32 imageSize = colorBuffer.surface.imageSize;
|
||||
uint32 alignment = colorBuffer.surface.alignment;
|
||||
|
||||
uint32 alignmentPaddingSize = (alignment - (imageSize%alignment)) % alignment;
|
||||
|
||||
|
||||
uint32 adjustedBufferingMode = bufferingMode;
|
||||
|
||||
uint32 bufferedImageSize = (imageSize + alignmentPaddingSize) * adjustedBufferingMode;
|
||||
bufferedImageSize = bufferedImageSize - alignmentPaddingSize;
|
||||
|
||||
memory_writeU32(sizeMPTR, bufferedImageSize);
|
||||
memory_writeU32(scaleNeededMPTR, 0);
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2SetDRCScale(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2SetDRCScale(%d,%d)", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2SetDRCConnectCallback(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamS32(channel, 0);
|
||||
ppcDefineParamMEMPTR(callback, void, 1);
|
||||
gx2Log_printf("GX2SetDRCConnectCallback(%d, 0x%08x)", channel, callback.GetMPTR());
|
||||
if(callback.GetPtr())
|
||||
PPCCoreCallback(callback, channel, TRUE);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2SetSemaphore(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2SetSemaphore(0x%08x,%d)", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
ppcDefineParamMPTR(semaphoreMPTR, 0);
|
||||
ppcDefineParamS32(mode, 1);
|
||||
|
||||
uint32 SEM_SEL;
|
||||
|
||||
if (mode == 0)
|
||||
{
|
||||
// wait
|
||||
SEM_SEL = 7;
|
||||
}
|
||||
else if (mode == 1)
|
||||
{
|
||||
// signal
|
||||
SEM_SEL = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
uint32 semaphoreControl = (SEM_SEL << 29);
|
||||
semaphoreControl |= 0x1000; // WAIT_ON_SIGNAL
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_MEM_SEMAPHORE, 2));
|
||||
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(semaphoreMPTR)); // semaphore physical address
|
||||
gx2WriteGather_submitU32AsBE(semaphoreControl); // control
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2Flush(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2Flush()");
|
||||
_GX2SubmitToTCL();
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
uint8* _GX2LastFlushPtr[PPC_CORE_COUNT] = {NULL};
|
||||
|
||||
uint64 _prevReturnedGPUTime = 0;
|
||||
|
||||
uint64 Latte_GetTime()
|
||||
{
|
||||
uint64 gpuTime = coreinit::coreinit_getTimerTick();
|
||||
gpuTime *= 20000ULL;
|
||||
if (gpuTime <= _prevReturnedGPUTime)
|
||||
gpuTime = _prevReturnedGPUTime + 1; // avoid ever returning identical timestamps
|
||||
_prevReturnedGPUTime = gpuTime;
|
||||
return gpuTime;
|
||||
}
|
||||
|
||||
void _GX2SubmitToTCL()
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
// do nothing if called from non-main GX2 core
|
||||
if (GX2::sGX2MainCoreIndex != coreIndex)
|
||||
{
|
||||
forceLogDebug_printf("_GX2SubmitToTCL() called on non-main GX2 core");
|
||||
return;
|
||||
}
|
||||
if( gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL )
|
||||
return; // quit if in display list
|
||||
_GX2LastFlushPtr[coreIndex] = (gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex]);
|
||||
// update last submitted CB timestamp
|
||||
uint64 commandBufferTimestamp = Latte_GetTime();
|
||||
LatteGPUState.lastSubmittedCommandBufferTimestamp.store(commandBufferTimestamp);
|
||||
gx2Log_printf("Submitting GX2 command buffer with timestamp %016I64x", commandBufferTimestamp);
|
||||
// submit HLE packet to write retirement timestamp
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_SET_CB_RETIREMENT_TIMESTAMP, 2));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(commandBufferTimestamp>>32ULL));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(commandBufferTimestamp&0xFFFFFFFFULL));
|
||||
}
|
||||
|
||||
uint32 _GX2GetUnflushedBytes(uint32 coreIndex)
|
||||
{
|
||||
uint32 unflushedBytes = 0;
|
||||
if (_GX2LastFlushPtr[coreIndex] != NULL)
|
||||
{
|
||||
if (_GX2LastFlushPtr[coreIndex] > gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex])
|
||||
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - gx2WriteGatherPipe.gxRingBuffer + 4); // this isn't 100% correct since we ignore the bytes between the last flush address and the start of the wrap around
|
||||
else
|
||||
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - _GX2LastFlushPtr[coreIndex]);
|
||||
}
|
||||
else
|
||||
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - gx2WriteGatherPipe.gxRingBuffer);
|
||||
return unflushedBytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Guarantees that the requested amount of space is available on the current command buffer
|
||||
* If the space is not available, the current command buffer is pushed to the GPU and a new one is allocated
|
||||
*/
|
||||
void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
// if we are in a display list then do nothing
|
||||
if( gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL )
|
||||
return;
|
||||
uint32 unflushedBytes = _GX2GetUnflushedBytes(coreIndex);
|
||||
if( unflushedBytes >= 0x1000 )
|
||||
{
|
||||
_GX2SubmitToTCL();
|
||||
}
|
||||
}
|
||||
|
||||
void gx2_load()
|
||||
{
|
||||
osLib_addFunction("gx2", "GX2GetContextStateDisplayList", gx2Export_GX2GetContextStateDisplayList);
|
||||
|
||||
// swap, vsync & timing
|
||||
osLib_addFunction("gx2", "GX2SwapScanBuffers", gx2Export_GX2SwapScanBuffers);
|
||||
osLib_addFunction("gx2", "GX2GetSwapStatus", gx2Export_GX2GetSwapStatus);
|
||||
osLib_addFunction("gx2", "GX2CopyColorBufferToScanBuffer", gx2Export_GX2CopyColorBufferToScanBuffer);
|
||||
osLib_addFunction("gx2", "GX2WaitForFreeScanBuffer", gx2Export_GX2WaitForFreeScanBuffer);
|
||||
osLib_addFunction("gx2", "GX2GetCurrentScanBuffer", gx2Export_GX2GetCurrentScanBuffer);
|
||||
|
||||
// shader stuff
|
||||
osLib_addFunction("gx2", "GX2GetVertexShaderGPRs", gx2Export_GX2GetVertexShaderGPRs);
|
||||
osLib_addFunction("gx2", "GX2GetVertexShaderStackEntries", gx2Export_GX2GetVertexShaderStackEntries);
|
||||
osLib_addFunction("gx2", "GX2GetPixelShaderGPRs", gx2Export_GX2GetPixelShaderGPRs);
|
||||
osLib_addFunction("gx2", "GX2GetPixelShaderStackEntries", gx2Export_GX2GetPixelShaderStackEntries);
|
||||
osLib_addFunction("gx2", "GX2SetFetchShader", gx2Export_GX2SetFetchShader);
|
||||
osLib_addFunction("gx2", "GX2SetVertexShader", gx2Export_GX2SetVertexShader);
|
||||
osLib_addFunction("gx2", "GX2SetPixelShader", gx2Export_GX2SetPixelShader);
|
||||
osLib_addFunction("gx2", "GX2SetGeometryShader", gx2Export_GX2SetGeometryShader);
|
||||
osLib_addFunction("gx2", "GX2SetComputeShader", gx2Export_GX2SetComputeShader);
|
||||
osLib_addFunction("gx2", "GX2SetVertexUniformReg", gx2Export_GX2SetVertexUniformReg);
|
||||
osLib_addFunction("gx2", "GX2SetVertexUniformBlock", gx2Export_GX2SetVertexUniformBlock);
|
||||
osLib_addFunction("gx2", "GX2RSetVertexUniformBlock", gx2Export_GX2RSetVertexUniformBlock);
|
||||
|
||||
osLib_addFunction("gx2", "GX2SetPixelUniformBlock", gx2Export_GX2SetPixelUniformBlock);
|
||||
osLib_addFunction("gx2", "GX2SetPixelUniformReg", gx2Export_GX2SetPixelUniformReg);
|
||||
osLib_addFunction("gx2", "GX2SetGeometryUniformBlock", gx2Export_GX2SetGeometryUniformBlock);
|
||||
osLib_addFunction("gx2", "GX2SetShaderModeEx", gx2Export_GX2SetShaderModeEx);
|
||||
|
||||
osLib_addFunction("gx2", "GX2CalcGeometryShaderInputRingBufferSize", gx2Export_GX2CalcGeometryShaderInputRingBufferSize);
|
||||
osLib_addFunction("gx2", "GX2CalcGeometryShaderOutputRingBufferSize", gx2Export_GX2CalcGeometryShaderOutputRingBufferSize);
|
||||
|
||||
// color/depth buffers
|
||||
osLib_addFunction("gx2", "GX2InitColorBufferRegs", gx2Export_GX2InitColorBufferRegs);
|
||||
osLib_addFunction("gx2", "GX2InitDepthBufferRegs", gx2Export_GX2InitDepthBufferRegs);
|
||||
osLib_addFunction("gx2", "GX2SetColorBuffer", gx2Export_GX2SetColorBuffer);
|
||||
osLib_addFunction("gx2", "GX2SetDepthBuffer", gx2Export_GX2SetDepthBuffer);
|
||||
|
||||
osLib_addFunction("gx2", "GX2SetDRCBuffer", gx2Export_GX2SetDRCBuffer);
|
||||
osLib_addFunction("gx2", "GX2MarkScanBufferCopied", gx2Export_GX2MarkScanBufferCopied);
|
||||
|
||||
// misc
|
||||
osLib_addFunction("gx2", "GX2TempGetGPUVersion", gx2Export_GX2TempGetGPUVersion);
|
||||
osLib_addFunction("gx2", "GX2CalcTVSize", gx2Export_GX2CalcTVSize);
|
||||
osLib_addFunction("gx2", "GX2CalcDRCSize", gx2Export_GX2CalcDRCSize);
|
||||
osLib_addFunction("gx2", "GX2SetDRCScale", gx2Export_GX2SetDRCScale);
|
||||
osLib_addFunction("gx2", "GX2SetDRCConnectCallback", gx2Export_GX2SetDRCConnectCallback);
|
||||
|
||||
osLib_addFunction("gx2", "GX2GetSystemTVScanMode", coreinitExport_GX2GetSystemTVScanMode);
|
||||
osLib_addFunction("gx2", "GX2GetSystemTVAspectRatio", coreinitExport_GX2GetSystemTVAspectRatio);
|
||||
|
||||
osLib_addFunction("gx2", "GX2SetSwapInterval", gx2Export_GX2SetSwapInterval);
|
||||
osLib_addFunction("gx2", "GX2GetSwapInterval", gx2Export_GX2GetSwapInterval);
|
||||
osLib_addFunction("gx2", "GX2GetGPUTimeout", gx2Export_GX2GetGPUTimeout);
|
||||
osLib_addFunction("gx2", "GX2SampleTopGPUCycle", gx2Export_GX2SampleTopGPUCycle);
|
||||
osLib_addFunction("gx2", "GX2SampleBottomGPUCycle", gx2Export_GX2SampleBottomGPUCycle);
|
||||
|
||||
osLib_addFunction("gx2", "GX2AllocateTilingApertureEx", gx2Export_GX2AllocateTilingApertureEx);
|
||||
osLib_addFunction("gx2", "GX2FreeTilingAperture", gx2Export_GX2FreeTilingAperture);
|
||||
|
||||
// context state
|
||||
osLib_addFunction("gx2", "GX2SetDefaultState", gx2Export_GX2SetDefaultState);
|
||||
osLib_addFunction("gx2", "GX2SetupContextStateEx", gx2Export_GX2SetupContextStateEx);
|
||||
osLib_addFunction("gx2", "GX2SetContextState", gx2Export_GX2SetContextState);
|
||||
|
||||
// semaphore
|
||||
osLib_addFunction("gx2", "GX2SetSemaphore", gx2Export_GX2SetSemaphore);
|
||||
|
||||
// command buffer
|
||||
osLib_addFunction("gx2", "GX2Flush", gx2Export_GX2Flush);
|
||||
|
||||
GX2::GX2Init_writeGather();
|
||||
GX2::GX2MemInit();
|
||||
GX2::GX2ResourceInit();
|
||||
GX2::GX2CommandInit();
|
||||
GX2::GX2SurfaceInit();
|
||||
GX2::GX2SurfaceCopyInit();
|
||||
GX2::GX2TextureInit();
|
||||
GX2::GX2StateInit();
|
||||
GX2::GX2ShaderInit();
|
||||
GX2::GX2EventInit();
|
||||
GX2::GX2BlitInit();
|
||||
GX2::GX2DrawInit();
|
||||
GX2::GX2StreamoutInit();
|
||||
GX2::GX2QueryInit();
|
||||
GX2::GX2MiscInit();
|
||||
}
|
||||
89
src/Cafe/OS/libs/gx2/GX2.h
Normal file
89
src/Cafe/OS/libs/gx2/GX2.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
#include "Cafe/HW/Latte/Core/LatteConst.h"
|
||||
|
||||
// base defines for GX2
|
||||
#define GX2_TRUE 1
|
||||
#define GX2_FALSE 0
|
||||
#define GX2_ENABLE 1
|
||||
#define GX2_DISABLE 0
|
||||
|
||||
// tex unit base for render backends
|
||||
#define CEMU_PS_TEX_UNIT_BASE 0
|
||||
#define CEMU_VS_TEX_UNIT_BASE 32
|
||||
#define CEMU_GS_TEX_UNIT_BASE 64
|
||||
|
||||
#include "GX2_Surface.h"
|
||||
|
||||
// general
|
||||
|
||||
void gx2_load();
|
||||
|
||||
// shader
|
||||
|
||||
void gx2Export_GX2SetFetchShader(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2GetVertexShaderGPRs(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2GetVertexShaderStackEntries(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2GetPixelShaderGPRs(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2GetPixelShaderStackEntries(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetVertexShader(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetPixelShader(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetGeometryShader(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetComputeShader(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetVertexUniformReg(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetVertexUniformBlock(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2RSetVertexUniformBlock(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetPixelUniformBlock(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetPixelUniformReg(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetGeometryUniformBlock(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetShaderModeEx(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2CalcGeometryShaderInputRingBufferSize(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2CalcGeometryShaderOutputRingBufferSize(PPCInterpreter_t* hCPU);
|
||||
|
||||
// write gather / command queue
|
||||
|
||||
#define GX2_COMMAND_RING_BUFFER_SIZE (64*1024*1024) // 64MB
|
||||
|
||||
void gx2Export_GX2GetContextStateDisplayList(PPCInterpreter_t* hCPU);
|
||||
|
||||
#include "GX2_Command.h"
|
||||
|
||||
// misc
|
||||
void gx2Export_GX2AllocateTilingApertureEx(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2FreeTilingAperture(PPCInterpreter_t* hCPU);
|
||||
|
||||
void gx2Export_GX2SetSwapInterval(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2GetSwapInterval(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2GetSwapStatus(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2GetGPUTimeout(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SampleTopGPUCycle(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SampleBottomGPUCycle(PPCInterpreter_t* hCPU);
|
||||
|
||||
// color/depth buffers
|
||||
|
||||
#define GX2_SCAN_TARGET_TV 1
|
||||
#define GX2_SCAN_TARGET_TV_RIGH 2
|
||||
#define GX2_SCAN_TARGET_DRC_FIRST 4
|
||||
#define GX2_SCAN_TARGET_DRC_SECOND 8
|
||||
|
||||
void gx2Export_GX2InitColorBufferRegs(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2InitDepthBufferRegs(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetColorBuffer(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetDepthBuffer(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetDRCBuffer(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2MarkScanBufferCopied(PPCInterpreter_t* hCPU);
|
||||
|
||||
// special state
|
||||
|
||||
#define GX2_SPECIAL_STATE_COUNT 9
|
||||
|
||||
// context state
|
||||
|
||||
void gx2Export_GX2SetDefaultState(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetupContextStateEx(PPCInterpreter_t* hCPU);
|
||||
void gx2Export_GX2SetContextState(PPCInterpreter_t* hCPU);
|
||||
|
||||
// command buffer
|
||||
|
||||
uint32 _GX2GetUnflushedBytes(uint32 coreIndex);
|
||||
void _GX2SubmitToTCL();
|
||||
void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32);
|
||||
422
src/Cafe/OS/libs/gx2/GX2_AddrTest.cpp
Normal file
422
src/Cafe/OS/libs/gx2/GX2_AddrTest.cpp
Normal file
|
|
@ -0,0 +1,422 @@
|
|||
#include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h"
|
||||
#include "Cafe/HW/Espresso/PPCCallback.h"
|
||||
#include "Cafe/HW/Espresso/PPCState.h"
|
||||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/HW/Latte/ISA/LatteReg.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.h"
|
||||
#include "util/highresolutiontimer/HighResolutionTimer.h"
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
struct AddrCreate_INPUT
|
||||
{
|
||||
/* +0x00 */ uint32be structSize;
|
||||
/* +0x04 */ uint32be ukn04_maybeGen;
|
||||
/* +0x08 */ uint32be ukn08;
|
||||
/* +0x0C */ uint32be revision;
|
||||
/* +0x10 */ uint32be func_Alloc;
|
||||
/* +0x14 */ uint32be func_Free;
|
||||
/* +0x18 */ uint32be func_Debug;
|
||||
/* +0x1C */ uint32be ukn1C;
|
||||
/* +0x20 */ uint32be reg263C;
|
||||
/* +0x24 */ uint32be ukn24;
|
||||
/* +0x28 */ uint32be ukn28;
|
||||
/* +0x2C */ uint32be ukn2C;
|
||||
/* +0x30 */ uint32be ukn30;
|
||||
/* +0x34 */ uint32be ukn34;
|
||||
/* +0x38 */ uint32be ukn38;
|
||||
/* +0x3C */ uint32be ukn3C;
|
||||
/* +0x40 */ uint32be ukn40;
|
||||
};
|
||||
|
||||
struct AddrCreate_OUTPUT
|
||||
{
|
||||
uint32be structSize;
|
||||
MEMPTR<void> addrLibPtr;
|
||||
};
|
||||
|
||||
static_assert(sizeof(AddrCreate_INPUT) == 0x44);
|
||||
static_assert(sizeof(AddrCreate_OUTPUT) == 8);
|
||||
|
||||
struct ADDRAllocParam
|
||||
{
|
||||
uint32be ukn00; // alignment?
|
||||
uint32be ukn04;
|
||||
uint32be size;
|
||||
};
|
||||
|
||||
struct ADDRComputeSurfaceInfo_INPUT
|
||||
{
|
||||
uint32be structSize;
|
||||
betype<Latte::E_HWTILEMODE> tileMode;
|
||||
betype<Latte::E_HWSURFFMT> format;
|
||||
uint32be bpp;
|
||||
uint32be numSamples;
|
||||
uint32be width;
|
||||
uint32be height;
|
||||
uint32be numSlices;
|
||||
uint32be slice;
|
||||
uint32be mipLevel;
|
||||
uint32be _flags;
|
||||
uint32be numFrags;
|
||||
MEMPTR<void> tileInfo;
|
||||
uint32be tileType;
|
||||
uint32be tileIndex;
|
||||
|
||||
enum FLAG_BITS
|
||||
{
|
||||
FLAG_BIT_CUBE = (1 << 27),
|
||||
FLAG_BIT_VOLUME = (1 << 26),
|
||||
|
||||
FLAG_BIT_OPT4SPACE = (1 << 19),
|
||||
};
|
||||
|
||||
void SetFlagCube(bool f)
|
||||
{
|
||||
if (f) _flags |= FLAG_BIT_CUBE;
|
||||
else _flags &= ~FLAG_BIT_CUBE;
|
||||
}
|
||||
|
||||
void SetFlagVolume(bool f)
|
||||
{
|
||||
if (f) _flags |= FLAG_BIT_VOLUME;
|
||||
else _flags &= ~FLAG_BIT_VOLUME;
|
||||
}
|
||||
|
||||
void SetFlagOpt4Space(bool f)
|
||||
{
|
||||
if (f) _flags |= FLAG_BIT_OPT4SPACE;
|
||||
else _flags &= ~FLAG_BIT_OPT4SPACE;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(ADDRComputeSurfaceInfo_INPUT) == 0x3C);
|
||||
|
||||
struct ADDRComputeSurfaceInfo_OUTPUT
|
||||
{
|
||||
/* 0x00 */ uint32be structSize;
|
||||
/* 0x04 */ uint32be pitch;
|
||||
/* 0x08 */ uint32be height;
|
||||
/* 0x0C */ uint32be depth;
|
||||
/* 0x10 */ uint64be surfSize;
|
||||
/* 0x18 */ uint32be tileMode;
|
||||
/* 0x1C */ uint32be baseAlign;
|
||||
/* 0x20 */ uint32be pitchAlign;
|
||||
/* 0x24 */ uint32be heightAlign;
|
||||
/* 0x28 */ uint32be depthAlign;
|
||||
/* 0x2C */ uint32be bpp;
|
||||
/* 0x30 */ uint32be pixelPitch;
|
||||
/* 0x34 */ uint32be pixelHeight;
|
||||
/* 0x38 */ uint32be pixelBits;
|
||||
/* 0x3C */ uint32be sliceSize;
|
||||
/* 0x40 */ uint32be pitchTileMax;
|
||||
/* 0x44 */ uint32be heightTileMax;
|
||||
/* 0x48 */ uint32be sliceTileMax;
|
||||
/* 0x4C */ MEMPTR<void> tileInfo;
|
||||
/* 0x50 */ uint32be tileType;
|
||||
/* 0x54 */ uint32be tileIndex;
|
||||
/* 0x58 */ MEMPTR<void> stereoInfo;
|
||||
/* 0x5C */ uint32be _padding;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ADDRComputeSurfaceInfo_OUTPUT) == 0x60);
|
||||
|
||||
static void _cb_alloc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamStructPtr(param, ADDRAllocParam, 0);
|
||||
uint32 r = coreinit_allocFromSysArea(param->size, 0x10);
|
||||
osLib_returnFromFunction(hCPU, r);
|
||||
}
|
||||
|
||||
static void _cb_free(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
static void _cb_debug(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
static void* sAddrLib{};
|
||||
static uint32be tclFunc_AddrCreate = 0;
|
||||
static uint32be tclFunc_AddrComputeSurfaceInfo = 0;
|
||||
|
||||
void _TestAddrLib_Init()
|
||||
{
|
||||
// load tcl_addr_test.rpl (from /cafelibs/)
|
||||
uint32be tclHandle;
|
||||
uint32 r = coreinit::OSDynLoad_Acquire("tcl_addr_test.rpl", &tclHandle);
|
||||
cemu_assert_debug(r == 0);
|
||||
|
||||
// get imports
|
||||
r = coreinit::OSDynLoad_FindExport(tclHandle, 0, "AddrCreate", &tclFunc_AddrCreate);
|
||||
cemu_assert_debug(r == 0);
|
||||
r = coreinit::OSDynLoad_FindExport(tclHandle, 0, "AddrComputeSurfaceInfo", &tclFunc_AddrComputeSurfaceInfo);
|
||||
cemu_assert_debug(r == 0);
|
||||
|
||||
// call AddrCreate
|
||||
StackAllocator<AddrCreate_INPUT> addrCreateIn;
|
||||
memset(addrCreateIn.GetPointer(), 0, sizeof(addrCreateIn));
|
||||
addrCreateIn->structSize = sizeof(addrCreateIn);
|
||||
|
||||
addrCreateIn->ukn04_maybeGen = 6; // R600?
|
||||
addrCreateIn->ukn08 = 0x51;
|
||||
addrCreateIn->revision = 71;
|
||||
addrCreateIn->reg263C = 0x44902;
|
||||
addrCreateIn->ukn24 = 0; // ukn
|
||||
|
||||
addrCreateIn->func_Alloc = PPCInterpreter_makeCallableExportDepr(_cb_alloc);
|
||||
addrCreateIn->func_Free = PPCInterpreter_makeCallableExportDepr(_cb_free);
|
||||
addrCreateIn->func_Debug = PPCInterpreter_makeCallableExportDepr(_cb_debug);
|
||||
|
||||
StackAllocator<AddrCreate_OUTPUT> addrCreateOut;
|
||||
memset(addrCreateOut.GetPointer(), 0, sizeof(addrCreateOut));
|
||||
addrCreateOut->structSize = sizeof(addrCreateOut);
|
||||
|
||||
r = PPCCoreCallback((uint32)tclFunc_AddrCreate, addrCreateIn.GetPointer(), addrCreateOut.GetPointer());
|
||||
sAddrLib = addrCreateOut->addrLibPtr;
|
||||
cemu_assert_debug(r == 0 && sAddrLib != nullptr);
|
||||
}
|
||||
|
||||
void _TestAddrLib_CalculateSurfaceInfo(Latte::E_GX2SURFFMT surfaceFormat, uint32 surfaceWidth, uint32 surfaceHeight, uint32 surfaceDepth, Latte::E_DIM surfaceDim, Latte::E_GX2TILEMODE surfaceTileMode, uint32 surfaceAA, uint32 level, ADDRComputeSurfaceInfo_OUTPUT* paramOut)
|
||||
{
|
||||
StackAllocator<ADDRComputeSurfaceInfo_INPUT> _paramIn;
|
||||
ADDRComputeSurfaceInfo_INPUT& paramIn = *_paramIn.GetPointer();
|
||||
memset(¶mIn, 0, sizeof(ADDRComputeSurfaceInfo_INPUT));
|
||||
memset(paramOut, 0, sizeof(ADDRComputeSurfaceInfo_OUTPUT));
|
||||
Latte::E_HWSURFFMT hwFormat = GetHWFormat(surfaceFormat);
|
||||
if (surfaceTileMode == Latte::E_GX2TILEMODE::TM_LINEAR_SPECIAL)
|
||||
{
|
||||
uint32 numSamples = 1 << surfaceAA;
|
||||
uint32 blockSize = IsCompressedFormat(surfaceFormat) ? 4 : 1;
|
||||
uint32 width = ((surfaceWidth >> level) + blockSize - 1) & ~(blockSize - 1);
|
||||
paramOut->bpp = GetFormatBits(hwFormat);
|
||||
paramOut->structSize = sizeof(ADDRComputeSurfaceInfo_OUTPUT);
|
||||
paramOut->pitch = width / blockSize;
|
||||
paramOut->pixelBits = paramOut->bpp;
|
||||
paramOut->baseAlign = 1;
|
||||
paramOut->pitchAlign = 1;
|
||||
paramOut->heightAlign = 1;
|
||||
paramOut->depthAlign = 1;
|
||||
switch (surfaceDim)
|
||||
{
|
||||
case Latte::E_DIM::DIM_1D:
|
||||
paramOut->height = 1;
|
||||
paramOut->depth = 1;
|
||||
break;
|
||||
case Latte::E_DIM::DIM_2D:
|
||||
paramOut->height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramOut->depth = 1;
|
||||
break;
|
||||
case Latte::E_DIM::DIM_3D:
|
||||
paramOut->height = surfaceHeight >> level;
|
||||
paramOut->height = std::max<uint32>(paramOut->height, 1);
|
||||
paramOut->depth = std::max<uint32>(surfaceDepth >> level, 1);
|
||||
break;
|
||||
case Latte::E_DIM::DIM_CUBEMAP:
|
||||
paramOut->height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramOut->depth = std::max<uint32>(surfaceDepth, 6);
|
||||
break;
|
||||
case Latte::E_DIM::DIM_1D_ARRAY:
|
||||
paramOut->height = 1;
|
||||
paramOut->depth = surfaceDepth;
|
||||
break;
|
||||
case Latte::E_DIM::DIM_2D_ARRAY:
|
||||
paramOut->height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramOut->depth = surfaceDepth;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
paramOut->height = ((paramOut->height + blockSize - 1) & ~(blockSize - 1)) / (uint64)blockSize;
|
||||
paramOut->pixelPitch = ((surfaceWidth >> level) + blockSize - 1) & ~(blockSize - 1);
|
||||
paramOut->pixelPitch = std::max<uint32>(paramOut->pixelPitch, blockSize);
|
||||
paramOut->pixelHeight = ((surfaceHeight >> level) + blockSize - 1) & ~(blockSize - 1);
|
||||
paramOut->pixelHeight = std::max<uint32>(paramOut->pixelHeight, blockSize);;
|
||||
paramOut->pitch = std::max<uint32>(paramOut->pitch, 1);
|
||||
paramOut->height = std::max<uint32>(paramOut->height, 1);
|
||||
paramOut->surfSize = paramOut->bpp * numSamples * paramOut->depth * paramOut->height * paramOut->pitch >> 3;
|
||||
if (surfaceDim == Latte::E_DIM::DIM_3D)
|
||||
paramOut->sliceSize = (uint32)(paramOut->surfSize);
|
||||
else
|
||||
{
|
||||
if (paramOut->surfSize == 0 && paramOut->depth == 0)
|
||||
paramOut->sliceSize = 0; // edge case for (1D)_ARRAY textures with 0/0/0 res
|
||||
else
|
||||
paramOut->sliceSize = ((uint32)paramOut->surfSize.value() / paramOut->depth);
|
||||
}
|
||||
paramOut->pitchTileMax = (paramOut->pitch >> 3) - 1;
|
||||
paramOut->heightTileMax = (paramOut->height >> 3) - 1;
|
||||
paramOut->sliceTileMax = (paramOut->height * paramOut->pitch >> 6) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
paramIn.structSize = sizeof(paramIn);
|
||||
paramIn.tileMode = Latte::MakeHWTileMode(surfaceTileMode);
|
||||
paramIn.format = hwFormat;
|
||||
paramIn.bpp = GetFormatBits(hwFormat);
|
||||
paramIn.numSamples = 1 << surfaceAA;
|
||||
paramIn.numFrags = paramIn.numSamples;
|
||||
paramIn.width = std::max<uint32>(surfaceWidth >> level, 1);
|
||||
switch (surfaceDim)
|
||||
{
|
||||
case Latte::E_DIM::DIM_1D:
|
||||
paramIn.height = 1;
|
||||
paramIn.numSlices = 1;
|
||||
break;
|
||||
case Latte::E_DIM::DIM_2D:
|
||||
paramIn.height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramIn.numSlices = 1;
|
||||
break;
|
||||
case Latte::E_DIM::DIM_3D:
|
||||
paramIn.height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramIn.numSlices = std::max<uint32>(surfaceDepth >> level, 1);
|
||||
break;
|
||||
case Latte::E_DIM::DIM_CUBEMAP:
|
||||
paramIn.height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramIn.numSlices = std::max<uint32>(surfaceDepth, 6);
|
||||
paramIn.SetFlagCube(true);
|
||||
break;
|
||||
case Latte::E_DIM::DIM_1D_ARRAY:
|
||||
paramIn.height = 1;
|
||||
paramIn.numSlices = surfaceDepth;
|
||||
break;
|
||||
case Latte::E_DIM::DIM_2D_ARRAY:
|
||||
paramIn.height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramIn.numSlices = surfaceDepth;
|
||||
break;
|
||||
case Latte::E_DIM::DIM_2D_MSAA:
|
||||
paramIn.height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramIn.numSlices = 1;
|
||||
break;
|
||||
case Latte::E_DIM::DIM_2D_ARRAY_MSAA:
|
||||
paramIn.height = std::max<uint32>(surfaceHeight >> level, 1);
|
||||
paramIn.numSlices = surfaceDepth;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
paramIn.slice = 0;
|
||||
paramIn.mipLevel = level;
|
||||
if (surfaceDim == Latte::E_DIM::DIM_3D)
|
||||
paramIn.SetFlagVolume(true);
|
||||
paramIn.SetFlagOpt4Space(level == 0);
|
||||
paramOut->structSize = sizeof(ADDRComputeSurfaceInfo_OUTPUT);
|
||||
PPCCoreCallback((uint32)tclFunc_AddrComputeSurfaceInfo, sAddrLib, _paramIn.GetPointer(), paramOut);
|
||||
}
|
||||
}
|
||||
|
||||
void _TestAddrLib_Compare(uint32 surfaceWidth, uint32 surfaceHeight, uint32 surfaceDepth, Latte::E_DIM surfaceDim, Latte::E_GX2SURFFMT surfaceFormat, Latte::E_GX2TILEMODE surfaceTileMode, uint32 surfaceAA, uint32 level)
|
||||
{
|
||||
// get result from tcl.rpl
|
||||
StackAllocator<ADDRComputeSurfaceInfo_OUTPUT> _paramOut;
|
||||
ADDRComputeSurfaceInfo_OUTPUT& tclSurfInfo = *_paramOut.GetPointer();
|
||||
_TestAddrLib_CalculateSurfaceInfo(surfaceFormat, surfaceWidth, surfaceHeight, surfaceDepth, surfaceDim, surfaceTileMode, surfaceAA, level, _paramOut.GetPointer());
|
||||
// get result from our implementation
|
||||
LatteAddrLib::AddrSurfaceInfo_OUT ourSurfInfo;
|
||||
LatteAddrLib::GX2CalculateSurfaceInfo(surfaceFormat, surfaceWidth, surfaceHeight, surfaceDepth, surfaceDim, surfaceTileMode, surfaceAA, level, &ourSurfInfo);
|
||||
// compare
|
||||
cemu_assert(tclSurfInfo.pitchAlign == ourSurfInfo.pitchAlign);
|
||||
cemu_assert((Latte::E_HWTILEMODE)tclSurfInfo.tileMode.value() == ourSurfInfo.hwTileMode);
|
||||
cemu_assert(tclSurfInfo.baseAlign == ourSurfInfo.baseAlign);
|
||||
cemu_assert(tclSurfInfo.surfSize == ourSurfInfo.surfSize);
|
||||
cemu_assert(tclSurfInfo.depthAlign == ourSurfInfo.depthAlign);
|
||||
cemu_assert(tclSurfInfo.pitch == ourSurfInfo.pitch);
|
||||
cemu_assert(tclSurfInfo.sliceSize == ourSurfInfo.sliceSize);
|
||||
}
|
||||
|
||||
void _TestAddrLib_Run()
|
||||
{
|
||||
uint32 surfaceAA = 0;
|
||||
|
||||
std::vector<Latte::E_DIM> dimList = {
|
||||
Latte::E_DIM::DIM_1D,
|
||||
Latte::E_DIM::DIM_2D,
|
||||
Latte::E_DIM::DIM_3D,
|
||||
Latte::E_DIM::DIM_CUBEMAP,
|
||||
Latte::E_DIM::DIM_1D_ARRAY,
|
||||
Latte::E_DIM::DIM_2D_ARRAY,
|
||||
Latte::E_DIM::DIM_2D_MSAA,
|
||||
Latte::E_DIM::DIM_2D_ARRAY_MSAA
|
||||
};
|
||||
|
||||
std::vector<Latte::E_GX2TILEMODE> tilemodeList = {
|
||||
// linear
|
||||
Latte::E_GX2TILEMODE::TM_LINEAR_GENERAL,
|
||||
Latte::E_GX2TILEMODE::TM_LINEAR_ALIGNED,
|
||||
// micro tiled
|
||||
Latte::E_GX2TILEMODE::TM_1D_TILED_THIN1,
|
||||
Latte::E_GX2TILEMODE::TM_1D_TILED_THICK,
|
||||
// macro tiled
|
||||
Latte::E_GX2TILEMODE::TM_2D_TILED_THIN1,
|
||||
Latte::E_GX2TILEMODE::TM_2D_TILED_THIN4,
|
||||
Latte::E_GX2TILEMODE::TM_2D_TILED_THIN2,
|
||||
Latte::E_GX2TILEMODE::TM_2D_TILED_THICK,
|
||||
Latte::E_GX2TILEMODE::TM_2B_TILED_THIN1,
|
||||
Latte::E_GX2TILEMODE::TM_2B_TILED_THIN2,
|
||||
Latte::E_GX2TILEMODE::TM_2B_TILED_THIN4,
|
||||
Latte::E_GX2TILEMODE::TM_2B_TILED_THICK,
|
||||
Latte::E_GX2TILEMODE::TM_3D_TILED_THIN1,
|
||||
Latte::E_GX2TILEMODE::TM_3D_TILED_THICK,
|
||||
Latte::E_GX2TILEMODE::TM_3B_TILED_THIN1,
|
||||
Latte::E_GX2TILEMODE::TM_3B_TILED_THICK,
|
||||
// special
|
||||
Latte::E_GX2TILEMODE::TM_LINEAR_SPECIAL,
|
||||
Latte::E_GX2TILEMODE::TM_32_SPECIAL, // note: Specific to GX2CalcSurfaceSizeAndAlignment, for AddrLib this should just be interpreted as (tm&0xF)
|
||||
};
|
||||
|
||||
std::vector<Latte::E_GX2SURFFMT> formatList = {
|
||||
Latte::E_GX2SURFFMT::HWFMT_8, Latte::E_GX2SURFFMT::HWFMT_8_8, Latte::E_GX2SURFFMT::HWFMT_8_8_8_8, // 8, 16, 32
|
||||
Latte::E_GX2SURFFMT::R32_UINT, Latte::E_GX2SURFFMT::R32_G32_UINT, Latte::E_GX2SURFFMT::R32_G32_B32_A32_UINT, // 32, 64, 128
|
||||
Latte::E_GX2SURFFMT::HWFMT_BC1, Latte::E_GX2SURFFMT::HWFMT_BC2, Latte::E_GX2SURFFMT::HWFMT_BC3, Latte::E_GX2SURFFMT::HWFMT_BC4, Latte::E_GX2SURFFMT::HWFMT_BC5
|
||||
};
|
||||
|
||||
std::vector<uint32> resXYList = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17,
|
||||
31, 32, 33, 50, 63, 64, 65, 127, 128, 129, 200, 253, 254, 255, 256, 257,
|
||||
511, 512, 513, 1023, 1024, 1025, 2047, 2048, 2049, 4095, 4096, 4097
|
||||
};
|
||||
|
||||
debug_printf("Running AddrLib test...\n");
|
||||
|
||||
BenchmarkTimer timer;
|
||||
timer.Start();
|
||||
size_t index = 0;
|
||||
for (auto dim : dimList)
|
||||
{
|
||||
debug_printf("%d/%d\n", (int)index, (int)dimList.size());
|
||||
index++;
|
||||
for (auto tileMode : tilemodeList)
|
||||
{
|
||||
for (auto format : formatList)
|
||||
{
|
||||
for (uint32 level = 0; level < 16; level++)
|
||||
{
|
||||
for (auto depth : resXYList)
|
||||
{
|
||||
for (auto height : resXYList)
|
||||
{
|
||||
for (auto width : resXYList)
|
||||
{
|
||||
_TestAddrLib_Compare(width, height, depth, dim, format, tileMode, surfaceAA, level);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
timer.Stop();
|
||||
debug_printf("Test complete (in %d seconds)\n", (int)(timer.GetElapsedMilliseconds() * 0.001));
|
||||
assert_dbg();
|
||||
}
|
||||
|
||||
void _test_AddrLib()
|
||||
{
|
||||
return;
|
||||
_TestAddrLib_Init();
|
||||
_TestAddrLib_Run();
|
||||
}
|
||||
}
|
||||
225
src/Cafe/OS/libs/gx2/GX2_Blit.cpp
Normal file
225
src/Cafe/OS/libs/gx2/GX2_Blit.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
#include "Common/precompiled.h"
|
||||
#include "GX2_Blit.h"
|
||||
#include "GX2_Command.h"
|
||||
#include "GX2_Surface.h"
|
||||
#include "Cafe/HW/Latte/ISA/LatteReg.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePM4.h"
|
||||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "GX2_Resource.h"
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
// sets the depth/stencil clear registers and updates clear values in DepthBuffer struct
|
||||
void GX2SetClearDepthStencil(GX2DepthBuffer* depthBuffer, float depthClearValue, uint8 stencilClearValue)
|
||||
{
|
||||
GX2ReserveCmdSpace(4);
|
||||
*(uint32*)&depthBuffer->clearDepth = _swapEndianU32(*(uint32*)&depthClearValue);
|
||||
depthBuffer->clearStencil = _swapEndianU32(stencilClearValue);
|
||||
Latte::LATTE_DB_STENCIL_CLEAR stencilClearReg;
|
||||
stencilClearReg.set_clearValue(stencilClearValue);
|
||||
Latte::LATTE_DB_DEPTH_CLEAR depthClearReg;
|
||||
depthClearReg.set_clearValue(depthClearValue);
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 2),
|
||||
Latte::REGADDR::DB_STENCIL_CLEAR - 0xA000,
|
||||
stencilClearReg, depthClearReg);
|
||||
}
|
||||
|
||||
// similar to GX2SetClearDepthStencil but only sets depth
|
||||
void GX2SetClearDepth(GX2DepthBuffer* depthBuffer, float depthClearValue)
|
||||
{
|
||||
GX2ReserveCmdSpace(3);
|
||||
*(uint32*)&depthBuffer->clearDepth = _swapEndianU32(*(uint32*)&depthClearValue);
|
||||
Latte::LATTE_DB_DEPTH_CLEAR depthClearReg;
|
||||
depthClearReg.set_clearValue(depthClearValue);
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
||||
Latte::REGADDR::DB_DEPTH_CLEAR - 0xA000,
|
||||
depthClearReg);
|
||||
}
|
||||
|
||||
// similar to GX2SetClearDepthStencil but only sets stencil
|
||||
void GX2SetClearStencil(GX2DepthBuffer* depthBuffer, uint8 stencilClearValue)
|
||||
{
|
||||
GX2ReserveCmdSpace(3);
|
||||
depthBuffer->clearStencil = _swapEndianU32(stencilClearValue);
|
||||
Latte::LATTE_DB_STENCIL_CLEAR stencilClearReg;
|
||||
stencilClearReg.set_clearValue(stencilClearValue);
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
||||
Latte::REGADDR::DB_STENCIL_CLEAR - 0xA000,
|
||||
stencilClearReg);
|
||||
}
|
||||
|
||||
// update DB_STENCIL_CLEAR and DB_STENCIL_CLEAR based on clear flags
|
||||
void _updateDepthStencilClearRegs(float depthClearValue, uint8 stencilClearValue, GX2ClearFlags clearFlags)
|
||||
{
|
||||
if ((clearFlags & GX2ClearFlags::SET_DEPTH_REG) != 0 && (clearFlags & GX2ClearFlags::SET_STENCIL_REG) != 0)
|
||||
{
|
||||
GX2ReserveCmdSpace(4);
|
||||
Latte::LATTE_DB_STENCIL_CLEAR stencilClearReg;
|
||||
stencilClearReg.set_clearValue(stencilClearValue);
|
||||
Latte::LATTE_DB_DEPTH_CLEAR depthClearReg;
|
||||
depthClearReg.set_clearValue(depthClearValue);
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 2),
|
||||
Latte::REGADDR::DB_STENCIL_CLEAR - 0xA000,
|
||||
stencilClearReg, depthClearReg);
|
||||
}
|
||||
else if ((clearFlags & GX2ClearFlags::SET_DEPTH_REG) != 0)
|
||||
{
|
||||
GX2ReserveCmdSpace(3);
|
||||
Latte::LATTE_DB_DEPTH_CLEAR depthClearReg;
|
||||
depthClearReg.set_clearValue(depthClearValue);
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
||||
Latte::REGADDR::DB_DEPTH_CLEAR - 0xA000,
|
||||
depthClearReg);
|
||||
}
|
||||
else if ((clearFlags & GX2ClearFlags::SET_STENCIL_REG) != 0)
|
||||
{
|
||||
GX2ReserveCmdSpace(3);
|
||||
Latte::LATTE_DB_STENCIL_CLEAR stencilClearReg;
|
||||
stencilClearReg.set_clearValue(stencilClearValue);
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
||||
Latte::REGADDR::DB_STENCIL_CLEAR - 0xA000,
|
||||
stencilClearReg);
|
||||
}
|
||||
}
|
||||
|
||||
void GX2ClearColor(GX2ColorBuffer* colorBuffer, float r, float g, float b, float a)
|
||||
{
|
||||
GX2ReserveCmdSpace(50);
|
||||
if ((colorBuffer->surface.resFlag & GX2_RESFLAG_USAGE_COLOR_BUFFER) != 0)
|
||||
{
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_CLEAR_COLOR_DEPTH_STENCIL, 23));
|
||||
gx2WriteGather_submitU32AsBE(1); // color (1)
|
||||
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(colorBuffer->surface.imagePtr));
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.format.value());
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.tileMode.value());
|
||||
gx2WriteGather_submitU32AsBE(colorBuffer->surface.width);
|
||||
gx2WriteGather_submitU32AsBE(colorBuffer->surface.height);
|
||||
gx2WriteGather_submitU32AsBE(colorBuffer->surface.pitch);
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(colorBuffer->viewFirstSlice));
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(colorBuffer->viewNumSlices));
|
||||
gx2WriteGather_submitU32AsBE(MPTR_NULL);
|
||||
gx2WriteGather_submitU32AsBE(0); // depth buffer format
|
||||
gx2WriteGather_submitU32AsBE(0); // tilemode for depth buffer
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE((uint32)(r * 255.0f));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(g * 255.0f));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(b * 255.0f));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(a * 255.0f));
|
||||
gx2WriteGather_submitU32AsBE(0); // clear depth
|
||||
gx2WriteGather_submitU32AsBE(0); // clear stencil
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf("GX2ClearColor() - unsupported surface flags\n");
|
||||
}
|
||||
}
|
||||
|
||||
void GX2ClearBuffersEx(GX2ColorBuffer* colorBuffer, GX2DepthBuffer* depthBuffer, float r, float g, float b, float a, float depthClearValue, uint8 stencilClearValue, GX2ClearFlags clearFlags)
|
||||
{
|
||||
GX2ReserveCmdSpace(50);
|
||||
_updateDepthStencilClearRegs(depthClearValue, stencilClearValue, clearFlags);
|
||||
|
||||
uint32 hleClearFlags = 0;
|
||||
if ((clearFlags & GX2ClearFlags::CLEAR_DEPTH) != 0)
|
||||
hleClearFlags |= 2;
|
||||
if ((clearFlags & GX2ClearFlags::CLEAR_STENCIL) != 0)
|
||||
hleClearFlags |= 4;
|
||||
hleClearFlags |= 1;
|
||||
|
||||
// send command to clear color, depth and stencil
|
||||
if (_swapEndianU32(colorBuffer->viewFirstSlice) != 0)
|
||||
debugBreakpoint();
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_CLEAR_COLOR_DEPTH_STENCIL, 23));
|
||||
gx2WriteGather_submitU32AsBE(hleClearFlags); // color (1), depth (2), stencil (4)
|
||||
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(colorBuffer->surface.imagePtr));
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.format.value());
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.tileMode.value());
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.width);
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.height);
|
||||
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.pitch);
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(colorBuffer->viewFirstSlice));
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(colorBuffer->viewNumSlices));
|
||||
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(depthBuffer->surface.imagePtr));
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.format.value());
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.tileMode.value());
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.width);
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.height);
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.pitch);
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(depthBuffer->viewFirstSlice));
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(depthBuffer->viewNumSlices));
|
||||
|
||||
gx2WriteGather_submitU32AsBE((uint32)(r * 255.0f));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(g * 255.0f));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(b * 255.0f));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(a * 255.0f));
|
||||
|
||||
gx2WriteGather_submitU32AsBE(*(uint32*)&depthClearValue); // clear depth
|
||||
gx2WriteGather_submitU32AsBE(stencilClearValue&0xFF); // clear stencil
|
||||
}
|
||||
|
||||
// always uses passed depthClearValue/stencilClearValue for clearing, even if clear flags dont specify value updates
|
||||
void GX2ClearDepthStencilEx(GX2DepthBuffer* depthBuffer, float depthClearValue, uint8 stencilClearValue, GX2ClearFlags clearFlags)
|
||||
{
|
||||
GX2ReserveCmdSpace(50);
|
||||
|
||||
if (!depthBuffer && (depthBuffer->surface.width == 0 || depthBuffer->surface.height == 0))
|
||||
{
|
||||
// Super Smash Bros tries to clear an uninitialized depth surface?
|
||||
debug_printf("GX2ClearDepthStencilEx(): Attempting to clear invalid depthbuffer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
_updateDepthStencilClearRegs(depthClearValue, stencilClearValue, clearFlags);
|
||||
|
||||
uint32 hleClearFlags = 0;
|
||||
if ((clearFlags & GX2ClearFlags::CLEAR_DEPTH) != 0)
|
||||
hleClearFlags |= 2;
|
||||
if ((clearFlags & GX2ClearFlags::CLEAR_STENCIL) != 0)
|
||||
hleClearFlags |= 4;
|
||||
|
||||
// send command to clear color, depth and stencil
|
||||
if (hleClearFlags != 0)
|
||||
{
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_CLEAR_COLOR_DEPTH_STENCIL, 23));
|
||||
gx2WriteGather_submitU32AsBE(hleClearFlags); // color (1), depth (2), stencil (4)
|
||||
gx2WriteGather_submitU32AsBE(MPTR_NULL);
|
||||
gx2WriteGather_submitU32AsBE(0); // format for color buffer
|
||||
gx2WriteGather_submitU32AsBE(0); // tilemode for color buffer
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(depthBuffer->surface.imagePtr));
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.format.value());
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.tileMode.value());
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.width);
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.height);
|
||||
gx2WriteGather_submitU32AsBE((uint32)depthBuffer->surface.pitch);
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(depthBuffer->viewFirstSlice));
|
||||
gx2WriteGather_submitU32AsBE(_swapEndianU32(depthBuffer->viewNumSlices));
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
|
||||
gx2WriteGather_submitU32AsBE(*(uint32*)&depthClearValue); // clear depth
|
||||
gx2WriteGather_submitU32AsBE(stencilClearValue & 0xFF); // clear stencil
|
||||
}
|
||||
}
|
||||
|
||||
void GX2BlitInit()
|
||||
{
|
||||
cafeExportRegister("gx2", GX2SetClearDepthStencil, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2SetClearDepth, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2SetClearStencil, LogType::GX2);
|
||||
|
||||
cafeExportRegister("gx2", GX2ClearColor, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2ClearBuffersEx, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2ClearDepthStencilEx, LogType::GX2);
|
||||
}
|
||||
}
|
||||
15
src/Cafe/OS/libs/gx2/GX2_Blit.h
Normal file
15
src/Cafe/OS/libs/gx2/GX2_Blit.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
enum class GX2ClearFlags : uint32
|
||||
{
|
||||
CLEAR_DEPTH = 0x01, // clear depth to given clear value
|
||||
CLEAR_STENCIL = 0x02, // clear stencil to given stencil clear value
|
||||
SET_DEPTH_REG = 0x04, //
|
||||
SET_STENCIL_REG = 0x08,
|
||||
};
|
||||
|
||||
void GX2BlitInit();
|
||||
}
|
||||
ENABLE_BITMASK_OPERATORS(GX2::GX2ClearFlags);
|
||||
308
src/Cafe/OS/libs/gx2/GX2_Command.cpp
Normal file
308
src/Cafe/OS/libs/gx2/GX2_Command.cpp
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePM4.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||
#include "GX2.h"
|
||||
#include "GX2_Command.h"
|
||||
#include "GX2_Shader.h"
|
||||
#include "GX2_Misc.h"
|
||||
|
||||
extern uint8* gxRingBufferReadPtr;
|
||||
|
||||
GX2WriteGatherPipeState gx2WriteGatherPipe = { 0 };
|
||||
|
||||
void gx2WriteGather_submitU32AsBE(uint32 v)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
if (gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] == NULL)
|
||||
return;
|
||||
*(uint32*)(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) = _swapEndianU32(v);
|
||||
(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) += 4;
|
||||
}
|
||||
|
||||
void gx2WriteGather_submitU32AsLE(uint32 v)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
if (gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] == NULL)
|
||||
return;
|
||||
*(uint32*)(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) = v;
|
||||
(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) += 4;
|
||||
}
|
||||
|
||||
void gx2WriteGather_submitU32AsLEArray(uint32* v, uint32 numValues)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
if (gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] == NULL)
|
||||
return;
|
||||
memcpy_dwords((*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]), v, numValues);
|
||||
(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) += 4 * numValues;
|
||||
}
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
sint32 gx2WriteGatherCurrentMainCoreIndex = -1;
|
||||
bool gx2WriteGatherInited = false;
|
||||
|
||||
void GX2Init_writeGather() // init write gather, make current core
|
||||
{
|
||||
if (gx2WriteGatherPipe.gxRingBuffer == NULL)
|
||||
gx2WriteGatherPipe.gxRingBuffer = (uint8*)malloc(GX2_COMMAND_RING_BUFFER_SIZE);
|
||||
if (gx2WriteGatherCurrentMainCoreIndex == sGX2MainCoreIndex)
|
||||
return; // write gather already configured for same core
|
||||
for (sint32 i = 0; i < PPC_CORE_COUNT; i++)
|
||||
{
|
||||
if (i == sGX2MainCoreIndex)
|
||||
{
|
||||
gx2WriteGatherPipe.writeGatherPtrGxBuffer[i] = gx2WriteGatherPipe.gxRingBuffer;
|
||||
gx2WriteGatherPipe.writeGatherPtrWrite[i] = &gx2WriteGatherPipe.writeGatherPtrGxBuffer[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
gx2WriteGatherPipe.writeGatherPtrGxBuffer[i] = NULL;
|
||||
gx2WriteGatherPipe.writeGatherPtrWrite[i] = NULL;
|
||||
}
|
||||
gx2WriteGatherPipe.displayListStart[i] = MPTR_NULL;
|
||||
gx2WriteGatherPipe.writeGatherPtrDisplayList[i] = NULL;
|
||||
gx2WriteGatherPipe.displayListMaxSize[i] = 0;
|
||||
}
|
||||
gx2WriteGatherCurrentMainCoreIndex = sGX2MainCoreIndex;
|
||||
gx2WriteGatherInited = true;
|
||||
}
|
||||
|
||||
void GX2WriteGather_beginDisplayList(PPCInterpreter_t* hCPU, MPTR buffer, uint32 maxSize)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
|
||||
gx2WriteGatherPipe.displayListStart[coreIndex] = buffer;
|
||||
gx2WriteGatherPipe.displayListMaxSize[coreIndex] = maxSize;
|
||||
// set new write gather ptr
|
||||
gx2WriteGatherPipe.writeGatherPtrDisplayList[coreIndex] = memory_getPointerFromVirtualOffset(gx2WriteGatherPipe.displayListStart[coreIndex]);
|
||||
gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] = &gx2WriteGatherPipe.writeGatherPtrDisplayList[coreIndex];
|
||||
}
|
||||
|
||||
uint32 GX2WriteGather_getDisplayListWriteDistance(sint32 coreIndex)
|
||||
{
|
||||
return (uint32)(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] - memory_getPointerFromVirtualOffset(gx2WriteGatherPipe.displayListStart[coreIndex]));
|
||||
}
|
||||
|
||||
uint32 GX2WriteGather_getFifoWriteDistance(uint32 coreIndex)
|
||||
{
|
||||
uint32 writeDistance = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - gx2WriteGatherPipe.gxRingBuffer);
|
||||
return writeDistance;
|
||||
}
|
||||
|
||||
uint32 GX2WriteGather_endDisplayList(PPCInterpreter_t* hCPU, MPTR buffer)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
|
||||
if (gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL)
|
||||
{
|
||||
uint32 currentWriteSize = GX2WriteGather_getDisplayListWriteDistance(coreIndex);
|
||||
// pad to 32 byte
|
||||
if (gx2WriteGatherPipe.displayListMaxSize[coreIndex] >= ((gx2WriteGatherPipe.displayListMaxSize[coreIndex] + 0x1F) & ~0x1F))
|
||||
{
|
||||
while ((currentWriteSize & 0x1F) != 0)
|
||||
{
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType2Filler());
|
||||
currentWriteSize += 4;
|
||||
}
|
||||
}
|
||||
// get size of written data
|
||||
currentWriteSize = GX2WriteGather_getDisplayListWriteDistance(coreIndex);
|
||||
// disable current display list and restore write gather ptr
|
||||
gx2WriteGatherPipe.displayListStart[coreIndex] = MPTR_NULL;
|
||||
if (sGX2MainCoreIndex == coreIndex)
|
||||
gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] = &gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex];
|
||||
else
|
||||
gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] = NULL;
|
||||
// return size of (written) display list
|
||||
return currentWriteSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no active display list
|
||||
// return a size of 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GX2GetCurrentDisplayList(betype<MPTR>* displayListAddr, uint32be* displayListSize)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
if (gx2WriteGatherPipe.displayListStart[coreIndex] == MPTR_NULL)
|
||||
return false;
|
||||
|
||||
if (displayListAddr)
|
||||
*displayListAddr = gx2WriteGatherPipe.displayListStart[coreIndex];
|
||||
if (displayListSize)
|
||||
*displayListSize = gx2WriteGatherPipe.displayListMaxSize[coreIndex];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GX2GetDisplayListWriteStatus()
|
||||
{
|
||||
// returns true if we are writing to a display list
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
return gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL;
|
||||
}
|
||||
|
||||
bool GX2WriteGather_isDisplayListActive()
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
if (gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 GX2WriteGather_getReadWriteDistance()
|
||||
{
|
||||
uint32 coreIndex = sGX2MainCoreIndex;
|
||||
uint32 writeDistance = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] + GX2_COMMAND_RING_BUFFER_SIZE - gxRingBufferReadPtr);
|
||||
writeDistance %= GX2_COMMAND_RING_BUFFER_SIZE;
|
||||
return writeDistance;
|
||||
}
|
||||
|
||||
void GX2WriteGather_checkAndInsertWrapAroundMark()
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
if (coreIndex != sGX2MainCoreIndex) // only if main gx2 core
|
||||
return;
|
||||
if (gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL)
|
||||
return;
|
||||
uint32 writeDistance = GX2WriteGather_getFifoWriteDistance(coreIndex);
|
||||
if (writeDistance >= (GX2_COMMAND_RING_BUFFER_SIZE * 3 / 5))
|
||||
{
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_FIFO_WRAP_AROUND, 1));
|
||||
gx2WriteGather_submitU32AsBE(0); // empty word since we can't send commands with zero data words
|
||||
gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] = gx2WriteGatherPipe.gxRingBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
void GX2BeginDisplayList(MEMPTR<void> displayListAddr, uint32 size)
|
||||
{
|
||||
GX2WriteGather_beginDisplayList(ppcInterpreterCurrentInstance, displayListAddr.GetMPTR(), size);
|
||||
}
|
||||
|
||||
void GX2BeginDisplayListEx(MEMPTR<void> displayListAddr, uint32 size, bool profiling)
|
||||
{
|
||||
GX2WriteGather_beginDisplayList(ppcInterpreterCurrentInstance, displayListAddr.GetMPTR(), size);
|
||||
}
|
||||
|
||||
uint32 GX2EndDisplayList(MEMPTR<void> displayListAddr)
|
||||
{
|
||||
cemu_assert_debug(displayListAddr != nullptr);
|
||||
uint32 displayListSize = GX2WriteGather_endDisplayList(ppcInterpreterCurrentInstance, displayListAddr.GetMPTR());
|
||||
return displayListSize;
|
||||
}
|
||||
|
||||
void GX2CallDisplayList(MPTR addr, uint32 size)
|
||||
{
|
||||
cemu_assert_debug((size&3) == 0);
|
||||
// write PM4 command
|
||||
GX2ReserveCmdSpace(4);
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_INDIRECT_BUFFER_PRIV, 3),
|
||||
memory_virtualToPhysical(addr),
|
||||
0, // high address bits
|
||||
size / 4);
|
||||
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
}
|
||||
|
||||
void GX2DirectCallDisplayList(void* addr, uint32 size)
|
||||
{
|
||||
// this API submits to TCL directly and bypasses write-gatherer
|
||||
// its basically a way to manually submit a command buffer to the GPU
|
||||
// as such it also affects the submission and retire timestamps
|
||||
|
||||
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
|
||||
cemu_assert_debug(coreIndex == sGX2MainCoreIndex);
|
||||
coreIndex = sGX2MainCoreIndex; // always submit to main queue which is owned by GX2 main core (TCLSubmitToRing does not need this workaround)
|
||||
|
||||
uint32be* cmdStream = (uint32be*)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex]);
|
||||
cmdStream[0] = pm4HeaderType3(IT_INDIRECT_BUFFER_PRIV, 3);
|
||||
cmdStream[1] = memory_virtualToPhysical(MEMPTR<void>(addr).GetMPTR());
|
||||
cmdStream[2] = 0;
|
||||
cmdStream[3] = size / 4;
|
||||
gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] += 16;
|
||||
|
||||
// update submission timestamp and retired timestamp
|
||||
_GX2SubmitToTCL();
|
||||
}
|
||||
|
||||
void GX2CopyDisplayList(MEMPTR<uint32be*> addr, uint32 size)
|
||||
{
|
||||
// copy display list to write gather
|
||||
uint32* displayListDWords = (uint32*)addr.GetPtr();
|
||||
uint32 dwordCount = size / 4;
|
||||
if (dwordCount > 0)
|
||||
{
|
||||
GX2ReserveCmdSpace(dwordCount);
|
||||
gx2WriteGather_submitU32AsLEArray(displayListDWords, dwordCount);
|
||||
}
|
||||
}
|
||||
|
||||
enum class GX2_PATCH_TYPE : uint32
|
||||
{
|
||||
FETCH_SHADER = 1,
|
||||
VERTEX_SHADER = 2,
|
||||
GEOMETRY_COPY_SHADER = 3,
|
||||
GEOMETRY_SHADER = 4,
|
||||
PIXEL_SHADER = 5,
|
||||
COMPUTE_SHADER = 6
|
||||
};
|
||||
|
||||
void GX2PatchDisplayList(uint32be* displayData, GX2_PATCH_TYPE patchType, uint32 patchOffset, void* obj)
|
||||
{
|
||||
cemu_assert_debug((patchOffset & 3) == 0);
|
||||
|
||||
if (patchType == GX2_PATCH_TYPE::VERTEX_SHADER)
|
||||
{
|
||||
GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)obj;
|
||||
displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(vertexShader->GetProgramAddr()) >> 8;
|
||||
}
|
||||
else if (patchType == GX2_PATCH_TYPE::PIXEL_SHADER)
|
||||
{
|
||||
GX2PixelShader_t* pixelShader = (GX2PixelShader_t*)obj;
|
||||
displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(pixelShader->GetProgramAddr()) >> 8;
|
||||
}
|
||||
else if (patchType == GX2_PATCH_TYPE::FETCH_SHADER)
|
||||
{
|
||||
GX2FetchShader_t* fetchShader = (GX2FetchShader_t*)obj;
|
||||
displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(fetchShader->GetProgramAddr()) >> 8;
|
||||
}
|
||||
else if (patchType == GX2_PATCH_TYPE::GEOMETRY_COPY_SHADER)
|
||||
{
|
||||
GX2GeometryShader_t* geometryShader = (GX2GeometryShader_t*)obj;
|
||||
displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(geometryShader->GetCopyProgramAddr()) >> 8;
|
||||
}
|
||||
else if (patchType == GX2_PATCH_TYPE::GEOMETRY_SHADER)
|
||||
{
|
||||
GX2GeometryShader_t* geometryShader = (GX2GeometryShader_t*)obj;
|
||||
displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(geometryShader->GetGeometryProgramAddr()) >> 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
forceLog_printf("GX2PatchDisplayList(): unsupported patchType %d", (uint32)patchType);
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GX2CommandInit()
|
||||
{
|
||||
|
||||
cafeExportRegister("gx2", GX2BeginDisplayList, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2BeginDisplayListEx, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2EndDisplayList, LogType::GX2);
|
||||
|
||||
cafeExportRegister("gx2", GX2GetCurrentDisplayList, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2GetDisplayListWriteStatus, LogType::GX2);
|
||||
|
||||
|
||||
cafeExportRegister("gx2", GX2CallDisplayList, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2DirectCallDisplayList, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2CopyDisplayList, LogType::GX2);
|
||||
|
||||
cafeExportRegister("gx2", GX2PatchDisplayList, LogType::GX2);
|
||||
}
|
||||
|
||||
}
|
||||
101
src/Cafe/OS/libs/gx2/GX2_Command.h
Normal file
101
src/Cafe/OS/libs/gx2/GX2_Command.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
#include "Cafe/HW/Latte/ISA/LatteReg.h"
|
||||
#include "Cafe/HW/Espresso/Const.h"
|
||||
|
||||
struct GX2WriteGatherPipeState
|
||||
{
|
||||
uint8* gxRingBuffer;
|
||||
// each core has it's own write gatherer and display list state (writing)
|
||||
uint8* writeGatherPtrGxBuffer[Espresso::CORE_COUNT];
|
||||
uint8** writeGatherPtrWrite[Espresso::CORE_COUNT];
|
||||
uint8* writeGatherPtrDisplayList[Espresso::CORE_COUNT];
|
||||
MPTR displayListStart[Espresso::CORE_COUNT];
|
||||
uint32 displayListMaxSize[Espresso::CORE_COUNT];
|
||||
};
|
||||
|
||||
extern GX2WriteGatherPipeState gx2WriteGatherPipe;
|
||||
|
||||
void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32); // move to GX2 namespace eventually
|
||||
|
||||
void gx2WriteGather_submitU32AsBE(uint32 v);
|
||||
void gx2WriteGather_submitU32AsLE(uint32 v);
|
||||
void gx2WriteGather_submitU32AsLEArray(uint32* v, uint32 numValues);
|
||||
|
||||
uint32 PPCInterpreter_getCurrentCoreIndex();
|
||||
|
||||
// gx2WriteGather_submit functions
|
||||
template <typename ...Targs>
|
||||
inline void gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr)
|
||||
{
|
||||
(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) = (uint8*)writePtr;
|
||||
}
|
||||
|
||||
template <typename T, typename ...Targs>
|
||||
inline void gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr, const betype<T>& arg, Targs... args)
|
||||
{
|
||||
static_assert(sizeof(betype<T>) == sizeof(uint32be));
|
||||
*(betype<T>*)writePtr = arg;
|
||||
writePtr++;
|
||||
gx2WriteGather_submit_(coreIndex, writePtr, args...);
|
||||
}
|
||||
|
||||
template <typename T, typename ...Targs>
|
||||
inline
|
||||
typename std::enable_if< std::is_floating_point<T>::value, void>::type
|
||||
gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr, const T& arg, Targs... args)
|
||||
{
|
||||
static_assert(sizeof(T) == sizeof(uint32));
|
||||
*writePtr = *(uint32*)&arg;
|
||||
writePtr++;
|
||||
gx2WriteGather_submit_(coreIndex, writePtr, args...);
|
||||
}
|
||||
|
||||
template <typename T, typename ...Targs>
|
||||
inline
|
||||
typename std::enable_if< std::is_base_of<Latte::LATTEREG, T>::value, void>::type
|
||||
gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr, const T& arg, Targs... args)
|
||||
{
|
||||
static_assert(sizeof(Latte::LATTEREG) == sizeof(uint32be));
|
||||
*writePtr = arg.getRawValue();
|
||||
writePtr++;
|
||||
gx2WriteGather_submit_(coreIndex, writePtr, args...);
|
||||
}
|
||||
|
||||
template <typename T, typename ...Targs>
|
||||
inline
|
||||
typename std::enable_if< !std::is_base_of<Latte::LATTEREG, T>::value && !std::is_floating_point<T>::value, void>::type
|
||||
gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr, const T& arg, Targs... args)
|
||||
{
|
||||
*writePtr = arg;
|
||||
writePtr++;
|
||||
gx2WriteGather_submit_(coreIndex, writePtr, args...);
|
||||
}
|
||||
|
||||
template <typename ...Targs>
|
||||
inline void gx2WriteGather_submit(Targs... args)
|
||||
{
|
||||
uint32 coreIndex = PPCInterpreter_getCurrentCoreIndex();
|
||||
if (gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] == nullptr)
|
||||
return;
|
||||
|
||||
uint32be* writePtr = (uint32be*)(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]);
|
||||
gx2WriteGather_submit_(coreIndex, writePtr, std::forward<Targs>(args)...);
|
||||
}
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
|
||||
bool GX2WriteGather_isDisplayListActive();
|
||||
uint32 GX2WriteGather_getReadWriteDistance();
|
||||
void GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
|
||||
void GX2BeginDisplayList(MEMPTR<void> displayListAddr, uint32 size);
|
||||
void GX2BeginDisplayListEx(MEMPTR<void> displayListAddr, uint32 size, bool profiling);
|
||||
uint32 GX2EndDisplayList(MEMPTR<void> displayListAddr);
|
||||
|
||||
void GX2CallDisplayList(MPTR addr, uint32 size);
|
||||
void GX2DirectCallDisplayList(void* addr, uint32 size);
|
||||
|
||||
void GX2Init_writeGather();
|
||||
void GX2CommandInit();
|
||||
}
|
||||
392
src/Cafe/OS/libs/gx2/GX2_ContextState.cpp
Normal file
392
src/Cafe/OS/libs/gx2/GX2_ContextState.cpp
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||
#include "GX2.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
|
||||
#include "Cafe/HW/Latte/Core/LattePM4.h"
|
||||
|
||||
#include "GX2_Command.h"
|
||||
#include "GX2_State.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
|
||||
#define GPU7_REG_AREA_SIZE_CONFIG_REG 0xB00
|
||||
#define GPU7_REG_AREA_SIZE_CONTEXT_REG 0x400
|
||||
#define GPU7_REG_AREA_SIZE_ALU_CONST 0x800
|
||||
#define GPU7_REG_AREA_SIZE_LOOP_CONST 0x60
|
||||
#define GPU7_REG_AREA_SIZE_RESOURCE 0xD9E
|
||||
#define GPU7_REG_AREA_SIZE_SAMPLER 0xA2 // (guessed)
|
||||
|
||||
#define _GX2_CALC_SHADOWMEM_NUM_U32(__v) (((((__v)*4)+0xFF)&~0xFF)/4)
|
||||
|
||||
MPTR gx2CurrentContextStateMPTR = MPTR_NULL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32 regOffset;
|
||||
uint32 regCount;
|
||||
}GX2RegLoadPktEntry_t;
|
||||
|
||||
GX2RegLoadPktEntry_t aluConst_loadPktEntries[1] = // base: 0xC000
|
||||
{
|
||||
{0, 0x800},
|
||||
};
|
||||
|
||||
GX2RegLoadPktEntry_t loopConst_loadPktEntries[1] = // base: 0xF880
|
||||
{
|
||||
{0, 0x60},
|
||||
};
|
||||
|
||||
GX2RegLoadPktEntry_t samplerReg_loadPktEntries[3] = // base: 0xF000
|
||||
{
|
||||
{0, 0x36},
|
||||
{0x36, 0x36},
|
||||
{0x6C, 0x36},
|
||||
};
|
||||
|
||||
GX2RegLoadPktEntry_t configReg_loadPktEntries[0xF] = // base: 0x2000
|
||||
{
|
||||
{0x300, 0x6},
|
||||
{0x900, 0x48},
|
||||
{0x980, 0x48},
|
||||
{0xA00, 0x48},
|
||||
{0x310, 0xC},
|
||||
{0x542, 0x1},
|
||||
{0x235, 0x1},
|
||||
{0x232, 0x2},
|
||||
{0x23A, 0x1},
|
||||
{0x256, 0x1},
|
||||
{0x60C, 0x1},
|
||||
{0x5C5, 0x1},
|
||||
{0x2C8, 0x1},
|
||||
{0x363, 0x1},
|
||||
{0x404, 0x2}
|
||||
};
|
||||
|
||||
GX2RegLoadPktEntry_t contextReg_loadPktEntries[0x2D] = // base: 0xA000
|
||||
{
|
||||
{0x0, 0x2},
|
||||
{0x3, 0x3},
|
||||
{0xA, 0x4},
|
||||
{0x10, 0x38},
|
||||
{0x50, 0x34},
|
||||
{0x8E, 0x4},
|
||||
{0x94, 0x40},
|
||||
{0x100, 0x9},
|
||||
{0x10C, 0x3},
|
||||
{0x10F, 0x60},
|
||||
{0x185, 0xA},
|
||||
{0x191, 0x27},
|
||||
{0x1E0, 0x9},
|
||||
{0x200, 0x1},
|
||||
{0x202, 0x7},
|
||||
{0xE0, 0x20},
|
||||
{0x210, 0x29},
|
||||
{0x250, 0x34},
|
||||
{0x290, 0x1},
|
||||
{0x292, 0x2},
|
||||
{0x2A1, 0x1},
|
||||
{0x2A5, 0x1},
|
||||
{0x2A8, 0x2},
|
||||
{0x2AC, 0x3},
|
||||
{0x2CA, 0x1},
|
||||
{0x2CC, 0x1},
|
||||
{0x2CE, 0x1},
|
||||
{0x300, 0x9},
|
||||
{0x30C, 0x1},
|
||||
{0x312, 0x1},
|
||||
{0x316, 0x2},
|
||||
{0x343, 0x2},
|
||||
{0x349, 0x3},
|
||||
{0x34C, 0x2},
|
||||
{0x351, 0x1},
|
||||
{0x37E, 0x6},
|
||||
{0x2B4, 0x3},
|
||||
{0x2B8, 0x3},
|
||||
{0x2BC, 0x3},
|
||||
{0x2C0, 0x3},
|
||||
{0x2C8, 0x1},
|
||||
{0x29B, 0x1},
|
||||
{0x8C, 0x1},
|
||||
{0xD5, 0x1},
|
||||
{0x284, 0xC}
|
||||
};
|
||||
|
||||
GX2RegLoadPktEntry_t resourceReg_loadPktEntries[9] = // base: 0xE000
|
||||
{
|
||||
{0, 0x70}, // ps tex
|
||||
{0x380, 0x70},
|
||||
{0x460, 0x70}, // vs tex
|
||||
{0x7E0, 0x70},
|
||||
{0x8B9, 0x7},
|
||||
{0x8C0, 0x70},
|
||||
{0x930, 0x70}, // gs tex
|
||||
{0xCB0, 0x70},
|
||||
{0xD89, 0x7}
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Hardware view of context state (register areas)
|
||||
uint32 areaConfigReg[_GX2_CALC_SHADOWMEM_NUM_U32(GPU7_REG_AREA_SIZE_CONFIG_REG)];
|
||||
uint32 areaContextReg[_GX2_CALC_SHADOWMEM_NUM_U32(GPU7_REG_AREA_SIZE_CONTEXT_REG)];
|
||||
uint32 areaALUConst[_GX2_CALC_SHADOWMEM_NUM_U32(GPU7_REG_AREA_SIZE_ALU_CONST)];
|
||||
uint32 areaLoopConst[_GX2_CALC_SHADOWMEM_NUM_U32(GPU7_REG_AREA_SIZE_LOOP_CONST)];
|
||||
uint32 areaResource[_GX2_CALC_SHADOWMEM_NUM_U32(GPU7_REG_AREA_SIZE_RESOURCE)];
|
||||
uint32 areaSampler[_GX2_CALC_SHADOWMEM_NUM_U32(GPU7_REG_AREA_SIZE_SAMPLER)];
|
||||
}GX2HwContextState_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GX2HwContextState_t hwContext;
|
||||
uint32 enableProfling;
|
||||
/* + 0x9804 */ uint32be loadDL_size;
|
||||
uint8 ukn9808[0x3FC-4];
|
||||
uint8 ukn9C00[0x200];
|
||||
/* +0x9E00 */ uint8 loadDL_buffer[0x300]; // this displaylist caches the IT_LOAD_* commands for swapping out context
|
||||
}GX2ContextState_t;
|
||||
|
||||
static_assert(offsetof(GX2ContextState_t, loadDL_size) == 0x9804);
|
||||
static_assert(sizeof(GX2ContextState_t) == 0xA100);
|
||||
|
||||
uint32 _GX2Context_CalcShadowMemSize(uint32 regCount)
|
||||
{
|
||||
return (regCount*4+0xFF)&~0xFF;
|
||||
}
|
||||
|
||||
uint32 _GX2Context_CalcStateSize()
|
||||
{
|
||||
uint32 contextStateSize = 0;
|
||||
contextStateSize += _GX2Context_CalcShadowMemSize(GPU7_REG_AREA_SIZE_CONFIG_REG);
|
||||
contextStateSize += _GX2Context_CalcShadowMemSize(GPU7_REG_AREA_SIZE_CONTEXT_REG);
|
||||
contextStateSize += _GX2Context_CalcShadowMemSize(GPU7_REG_AREA_SIZE_ALU_CONST);
|
||||
contextStateSize += _GX2Context_CalcShadowMemSize(GPU7_REG_AREA_SIZE_LOOP_CONST);
|
||||
contextStateSize += _GX2Context_CalcShadowMemSize(GPU7_REG_AREA_SIZE_RESOURCE);
|
||||
contextStateSize += _GX2Context_CalcShadowMemSize(GPU7_REG_AREA_SIZE_SAMPLER);
|
||||
return contextStateSize;
|
||||
}
|
||||
|
||||
void _GX2Context_CreateLoadDL()
|
||||
{
|
||||
GX2ReserveCmdSpace(3);
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_CONTEXT_CONTROL, 2));
|
||||
gx2WriteGather_submitU32AsBE(0x80000077);
|
||||
gx2WriteGather_submitU32AsBE(0x80000077);
|
||||
}
|
||||
|
||||
void _GX2Context_WriteCmdDisableStateShadowing()
|
||||
{
|
||||
GX2ReserveCmdSpace(3);
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_CONTEXT_CONTROL, 2));
|
||||
gx2WriteGather_submitU32AsBE(0x80000000);
|
||||
gx2WriteGather_submitU32AsBE(0x80000000);
|
||||
}
|
||||
|
||||
void _GX2Context_cmdLoad(void* gx2ukn, uint32 pm4Header, MPTR physAddrRegArea, uint32 waitForIdle, uint32 numRegOffsetEntries, GX2RegLoadPktEntry_t* regOffsetEntries)
|
||||
{
|
||||
GX2ReserveCmdSpace(3 + numRegOffsetEntries*2);
|
||||
gx2WriteGather_submitU32AsBE(pm4Header);
|
||||
gx2WriteGather_submitU32AsBE(physAddrRegArea);
|
||||
gx2WriteGather_submitU32AsBE(waitForIdle);
|
||||
for(uint32 i=0; i<numRegOffsetEntries; i++)
|
||||
{
|
||||
gx2WriteGather_submitU32AsBE(regOffsetEntries[i].regOffset); // regOffset
|
||||
gx2WriteGather_submitU32AsBE(regOffsetEntries[i].regCount); // regCount
|
||||
}
|
||||
}
|
||||
|
||||
#define __cmdStateLoad(__gx2State, __pm4Command, __regArea, __waitForIdle, __regLoadPktEntries) _GX2Context_cmdLoad(NULL, pm4HeaderType3(__pm4Command, 2+sizeof(__regLoadPktEntries)/sizeof(__regLoadPktEntries[0])*2), memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(__regArea)), __waitForIdle, sizeof(__regLoadPktEntries)/sizeof(__regLoadPktEntries[0]), __regLoadPktEntries)
|
||||
|
||||
void _GX2Context_WriteCmdRestoreState(GX2ContextState_t* gx2ContextState, uint32 ukn)
|
||||
{
|
||||
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
MPTR physAddrContextState = memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(gx2ContextState));
|
||||
_GX2Context_CreateLoadDL();
|
||||
__cmdStateLoad(NULL, IT_LOAD_CONFIG_REG, gx2ContextState->hwContext.areaConfigReg, 0x80000000, configReg_loadPktEntries);
|
||||
__cmdStateLoad(NULL, IT_LOAD_CONTEXT_REG, gx2ContextState->hwContext.areaContextReg, 0, contextReg_loadPktEntries);
|
||||
__cmdStateLoad(NULL, IT_LOAD_ALU_CONST, gx2ContextState->hwContext.areaALUConst, 0, aluConst_loadPktEntries);
|
||||
__cmdStateLoad(NULL, IT_LOAD_LOOP_CONST, gx2ContextState->hwContext.areaLoopConst, 0, loopConst_loadPktEntries);
|
||||
__cmdStateLoad(NULL, IT_LOAD_RESOURCE, gx2ContextState->hwContext.areaResource, 0, resourceReg_loadPktEntries);
|
||||
__cmdStateLoad(NULL, IT_LOAD_SAMPLER, gx2ContextState->hwContext.areaSampler, 0, samplerReg_loadPktEntries);
|
||||
}
|
||||
|
||||
void GX2SetDefaultState()
|
||||
{
|
||||
GX2ReserveCmdSpace(0x100);
|
||||
|
||||
Latte::LATTE_PA_CL_VTE_CNTL reg{};
|
||||
reg.set_VPORT_X_OFFSET_ENA(true).set_VPORT_X_SCALE_ENA(true);
|
||||
reg.set_VPORT_Y_OFFSET_ENA(true).set_VPORT_Y_SCALE_ENA(true);
|
||||
reg.set_VPORT_Z_OFFSET_ENA(true).set_VPORT_Z_SCALE_ENA(true);
|
||||
reg.set_VTX_W0_FMT(true);
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
||||
Latte::REGADDR::PA_CL_VTE_CNTL - 0xA000,
|
||||
reg);
|
||||
|
||||
uint32 stencilTestEnable = GX2_FALSE;
|
||||
uint32 backStencilEnable = GX2_FALSE;
|
||||
uint32 frontStencilFunc = 0;
|
||||
uint32 frontStencilZPass = 0;
|
||||
uint32 frontStencilZFail = 0;
|
||||
uint32 frontStencilFail = 0;
|
||||
uint32 backStencilFunc = 0;
|
||||
uint32 backStencilZPass = 0;
|
||||
uint32 backStencilZFail = 0;
|
||||
uint32 backStencilFail = 0;
|
||||
|
||||
uint32 depthControlReg = 0;
|
||||
// depth stuff
|
||||
depthControlReg |= (1<<1);
|
||||
depthControlReg |= (1<<2);
|
||||
depthControlReg |= ((1&7)<<4);
|
||||
// stencil stuff
|
||||
depthControlReg |= ((stencilTestEnable&1)<<0);
|
||||
depthControlReg |= ((backStencilEnable&1)<<7);
|
||||
depthControlReg |= ((frontStencilFunc&7)<<8);
|
||||
depthControlReg |= ((frontStencilZPass&7)<<14);
|
||||
depthControlReg |= ((frontStencilZFail&7)<<17);
|
||||
depthControlReg |= ((frontStencilFail&7)<<11);
|
||||
depthControlReg |= ((backStencilFunc&7)<<20);
|
||||
depthControlReg |= ((backStencilZPass&7)<<26);
|
||||
depthControlReg |= ((backStencilZFail&3)<<29);
|
||||
depthControlReg |= ((backStencilFail&7)<<23);
|
||||
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1));
|
||||
gx2WriteGather_submitU32AsBE(Latte::REGADDR::DB_DEPTH_CONTROL-0xA000);
|
||||
gx2WriteGather_submitU32AsBE(depthControlReg);
|
||||
|
||||
GX2::GX2SetAlphaTest(GX2_DISABLE, GX2::GX2_ALPHAFUNC::LESS, 0.0f);
|
||||
GX2::GX2SetPolygonControl(Latte::LATTE_PA_SU_SC_MODE_CNTL::E_FRONTFACE::CCW, GX2_DISABLE, GX2_DISABLE, Latte::LATTE_PA_SU_SC_MODE_CNTL::E_POLYGONMODE::UKN0, Latte::LATTE_PA_SU_SC_MODE_CNTL::E_PTYPE::TRIANGLES, Latte::LATTE_PA_SU_SC_MODE_CNTL::E_PTYPE::TRIANGLES, GX2_DISABLE, GX2_DISABLE, GX2_DISABLE);
|
||||
GX2::GX2SetPolygonOffset(0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
GX2::GX2SetPrimitiveRestartIndex(0xffffffff);
|
||||
GX2::GX2SetTargetChannelMasks(0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF);
|
||||
GX2::GX2SetBlendConstantColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
GX2::GX2SetPointSize(1.0f, 1.0f);
|
||||
GX2::GX2SetPointLimits(1.0f, 1.0f);
|
||||
GX2::GX2SetColorControl(GX2::GX2_LOGICOP::COPY, GX2_DISABLE, GX2_DISABLE, GX2_ENABLE);
|
||||
GX2::GX2SetRasterizerClipControlEx(true, true, false);
|
||||
|
||||
// Set clear depth to 1.0 (workaround for Darksiders 2. Investigate whether actual GX2 driver also sets this)
|
||||
float depth = 1.0;
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1));
|
||||
gx2WriteGather_submitU32AsBE(mmDB_DEPTH_CLEAR - 0xA000);
|
||||
gx2WriteGather_submitU32AsBE(*(uint32*)&depth); // depth (as float)
|
||||
|
||||
// reset HLE special states
|
||||
for (sint32 i = 0; i <= GX2_SPECIAL_STATE_COUNT; i++)
|
||||
{
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_SPECIAL_STATE, 2));
|
||||
gx2WriteGather_submitU32AsBE(i); // state id
|
||||
gx2WriteGather_submitU32AsBE(0); // disable
|
||||
}
|
||||
}
|
||||
|
||||
void gx2Export_GX2SetDefaultState(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2SetDefaultState()");
|
||||
GX2SetDefaultState();
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void _GX2ContextCreateRestoreStateDL(GX2ContextState_t* gx2ContextState)
|
||||
{
|
||||
// begin display list
|
||||
if (GX2::GX2WriteGather_isDisplayListActive())
|
||||
assert_dbg();
|
||||
GX2::GX2BeginDisplayList((void*)gx2ContextState->loadDL_buffer, sizeof(gx2ContextState->loadDL_buffer));
|
||||
_GX2Context_WriteCmdRestoreState(gx2ContextState, 0);
|
||||
uint32 displayListSize = GX2::GX2EndDisplayList((void*)gx2ContextState->loadDL_buffer);
|
||||
gx2ContextState->loadDL_size = displayListSize;
|
||||
}
|
||||
|
||||
void gx2Export_GX2SetupContextStateEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2SetupContextStateEx(0x%08x)\n", hCPU->gpr[3]);
|
||||
cemu_assert_debug(hCPU->gpr[4] == 0 || hCPU->gpr[4] == 1);
|
||||
|
||||
GX2ContextState_t* gx2ContextState = (GX2ContextState_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
|
||||
uint32 hwContextSize = _GX2Context_CalcStateSize();
|
||||
if( hwContextSize != sizeof(GX2HwContextState_t) )
|
||||
assert_dbg();
|
||||
if( sizeof(GX2HwContextState_t) != 0x9800 )
|
||||
assert_dbg(); // GX2 HW context size mismatch
|
||||
if( sizeof(GX2ContextState_t) != 0xA100 )
|
||||
assert_dbg(); // GX2 context size mismatch
|
||||
|
||||
memset(gx2ContextState, 0x00, sizeof(GX2ContextState_t));
|
||||
gx2ContextState->enableProfling = _swapEndianU32(hCPU->gpr[4]&1);
|
||||
_GX2Context_WriteCmdRestoreState(gx2ContextState, 1);
|
||||
|
||||
gx2CurrentContextStateMPTR = hCPU->gpr[3];
|
||||
|
||||
_GX2Context_CreateLoadDL();
|
||||
GX2SetDefaultState();
|
||||
_GX2ContextCreateRestoreStateDL(gx2ContextState);
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void gx2Export_GX2SetContextState(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2SetContextState(0x%08x)\n", hCPU->gpr[3]);
|
||||
// parameters:
|
||||
if( hCPU->gpr[3] == MPTR_NULL )
|
||||
{
|
||||
// disable state shadowing
|
||||
_GX2Context_WriteCmdDisableStateShadowing();
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
// check if context state changed
|
||||
bool boiWorkaround = CafeSystem::GetRPXHashBase() == 0x6BCD618E; // workaround for a bug in Binding of Isaac to avoid flicker
|
||||
if( boiWorkaround )
|
||||
{
|
||||
if( hCPU->gpr[3] != gx2CurrentContextStateMPTR ) // dont reload same state
|
||||
{
|
||||
GX2ContextState_t* gx2ContextState = (GX2ContextState_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
_GX2Context_WriteCmdRestoreState(gx2ContextState, 0);
|
||||
_GX2Context_CreateLoadDL();
|
||||
// set new context state
|
||||
gx2CurrentContextStateMPTR = hCPU->gpr[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
// even if it's the same context, make sure state shadowing is enabled.
|
||||
_GX2Context_CreateLoadDL();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GX2ContextState_t* gx2ContextState = (GX2ContextState_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
|
||||
if (gx2ContextState->loadDL_size == 0)
|
||||
{
|
||||
_GX2Context_CreateLoadDL();
|
||||
_GX2Context_WriteCmdRestoreState(gx2ContextState, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_GX2Context_CreateLoadDL();
|
||||
GX2::GX2CallDisplayList(memory_getVirtualOffsetFromPointer(gx2ContextState->loadDL_buffer), gx2ContextState->loadDL_size);
|
||||
}
|
||||
// set new context state
|
||||
gx2CurrentContextStateMPTR = hCPU->gpr[3];
|
||||
|
||||
}
|
||||
// todo: Save/restore GX2 special state as well -> handle this by correctly emulating the state
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
|
||||
void gx2Export_GX2GetContextStateDisplayList(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2GetContextStateDisplayList(0x%08x, 0x%08x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
||||
ppcDefineParamStructPtr(gx2ContextState, GX2ContextState_t, 0);
|
||||
ppcDefineParamU32BEPtr(displayListPtrOut, 1);
|
||||
ppcDefineParamU32BEPtr(displayListSizeOut, 2);
|
||||
|
||||
*displayListPtrOut = memory_getVirtualOffsetFromPointer(gx2ContextState->loadDL_buffer);
|
||||
*displayListSizeOut = gx2ContextState->loadDL_size;
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
231
src/Cafe/OS/libs/gx2/GX2_Draw.cpp
Normal file
231
src/Cafe/OS/libs/gx2/GX2_Draw.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||
#include "GX2.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
#include "Cafe/HW/Latte/ISA/LatteReg.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePM4.h"
|
||||
|
||||
#include "Cafe/OS/common/OSCommon.h"
|
||||
|
||||
#include "GX2_Command.h"
|
||||
#include "GX2_Draw.h"
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
void GX2SetAttribBuffer(uint32 bufferIndex, uint32 sizeInBytes, uint32 stride, void* data)
|
||||
{
|
||||
GX2ReserveCmdSpace(9);
|
||||
MPTR physicalAddress = memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(data));
|
||||
// write PM4 command
|
||||
gx2WriteGather_submit(
|
||||
pm4HeaderType3(IT_SET_RESOURCE, 8),
|
||||
0x8C0 + bufferIndex * 7,
|
||||
physicalAddress,
|
||||
sizeInBytes - 1, // size
|
||||
(stride & 0xFFFF) << 11, // stride
|
||||
0, // ukn
|
||||
0, // ukn
|
||||
0, // ukn
|
||||
0xC0000000); // ukn
|
||||
}
|
||||
|
||||
void GX2DrawIndexedEx(GX2PrimitiveMode2 primitiveMode, uint32 count, GX2IndexType indexType, void* indexData, uint32 baseVertex, uint32 numInstances)
|
||||
{
|
||||
GX2ReserveCmdSpace(3 + 3 + 2 + 2 + 6);
|
||||
gx2WriteGather_submit(
|
||||
// IT_SET_CTL_CONST
|
||||
pm4HeaderType3(IT_SET_CTL_CONST, 2), 0,
|
||||
baseVertex,
|
||||
// IT_SET_CONFIG_REG
|
||||
pm4HeaderType3(IT_SET_CONFIG_REG, 2), Latte::REGADDR::VGT_PRIMITIVE_TYPE - 0x2000,
|
||||
(uint32)primitiveMode,
|
||||
// IT_INDEX_TYPE
|
||||
pm4HeaderType3(IT_INDEX_TYPE, 1),
|
||||
(uint32)indexType,
|
||||
// IT_NUM_INSTANCES
|
||||
pm4HeaderType3(IT_NUM_INSTANCES, 1),
|
||||
numInstances,
|
||||
// IT_DRAW_INDEX_2
|
||||
pm4HeaderType3(IT_DRAW_INDEX_2, 5) | 0x00000001,
|
||||
-1,
|
||||
memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(indexData)),
|
||||
0,
|
||||
count,
|
||||
0);
|
||||
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
}
|
||||
|
||||
void GX2DrawIndexedEx2(GX2PrimitiveMode2 primitiveMode, uint32 count, GX2IndexType indexType, void* indexData, uint32 baseVertex, uint32 numInstances, uint32 baseInstance)
|
||||
{
|
||||
GX2ReserveCmdSpace(3 + 3 + 2 + 2 + 6);
|
||||
gx2WriteGather_submit(
|
||||
// IT_SET_CTL_CONST
|
||||
pm4HeaderType3(IT_SET_CTL_CONST, 2), 0,
|
||||
baseVertex,
|
||||
// set base instance
|
||||
pm4HeaderType3(IT_SET_CTL_CONST, 2), 1,
|
||||
baseInstance,
|
||||
// IT_SET_CONFIG_REG
|
||||
pm4HeaderType3(IT_SET_CONFIG_REG, 2), Latte::REGADDR::VGT_PRIMITIVE_TYPE - 0x2000,
|
||||
(uint32)primitiveMode,
|
||||
// IT_INDEX_TYPE
|
||||
pm4HeaderType3(IT_INDEX_TYPE, 1),
|
||||
(uint32)indexType,
|
||||
// IT_NUM_INSTANCES
|
||||
pm4HeaderType3(IT_NUM_INSTANCES, 1),
|
||||
numInstances,
|
||||
// IT_DRAW_INDEX_2
|
||||
pm4HeaderType3(IT_DRAW_INDEX_2, 5) | 0x00000001,
|
||||
-1,
|
||||
memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(indexData)),
|
||||
0,
|
||||
count,
|
||||
0,
|
||||
// reset base instance
|
||||
pm4HeaderType3(IT_SET_CTL_CONST, 2), 1,
|
||||
0 // baseInstance
|
||||
);
|
||||
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
}
|
||||
|
||||
void GX2DrawEx(GX2PrimitiveMode2 primitiveMode, uint32 count, uint32 baseVertex, uint32 numInstances)
|
||||
{
|
||||
GX2ReserveCmdSpace(3 + 3 + 2 + 2 + 6);
|
||||
gx2WriteGather_submit(
|
||||
// IT_SET_CTL_CONST
|
||||
pm4HeaderType3(IT_SET_CTL_CONST, 2), 0,
|
||||
baseVertex,
|
||||
// IT_SET_CONFIG_REG
|
||||
pm4HeaderType3(IT_SET_CONFIG_REG, 2), Latte::REGADDR::VGT_PRIMITIVE_TYPE - 0x2000,
|
||||
(uint32)primitiveMode,
|
||||
// IT_INDEX_TYPE
|
||||
pm4HeaderType3(IT_INDEX_TYPE, 1),
|
||||
(uint32)GX2IndexType::U32_BE,
|
||||
// IT_NUM_INSTANCES
|
||||
pm4HeaderType3(IT_NUM_INSTANCES, 1),
|
||||
numInstances,
|
||||
// IT_DRAW_INDEX_2
|
||||
pm4HeaderType3(IT_DRAW_INDEX_AUTO, 2) | 0x00000001,
|
||||
count,
|
||||
0 // DRAW_INITIATOR
|
||||
);
|
||||
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
}
|
||||
|
||||
void GX2DrawIndexedImmediateEx(GX2PrimitiveMode2 primitiveMode, uint32 count, GX2IndexType indexType, void* indexData, uint32 baseVertex, uint32 numInstances)
|
||||
{
|
||||
uint32* indexDataU32 = (uint32*)indexData;
|
||||
uint32 numIndexU32s;
|
||||
bool use32BitIndices = false;
|
||||
if (indexType == GX2IndexType::U16_BE || indexType == GX2IndexType::U16_LE)
|
||||
{
|
||||
// 16bit indices
|
||||
numIndexU32s = (count + 1) / 2;
|
||||
}
|
||||
else if (indexType == GX2IndexType::U32_BE || indexType == GX2IndexType::U32_LE)
|
||||
{
|
||||
// 32bit indices
|
||||
numIndexU32s = count;
|
||||
use32BitIndices = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
GX2ReserveCmdSpace(3 + 3 + 3 + 2 + 2 + 6 + 3 + numIndexU32s);
|
||||
|
||||
if (numIndexU32s > 0x4000 - 2)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "GX2DrawIndexedImmediateEx(): Draw exceeds maximum PM4 command size. Keep index size below 16KiB minus 8 byte");
|
||||
return;
|
||||
}
|
||||
|
||||
// set base vertex
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CTL_CONST, 2));
|
||||
gx2WriteGather_submitU32AsBE(0);
|
||||
gx2WriteGather_submitU32AsBE(baseVertex);
|
||||
// set primitive mode
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONFIG_REG, 2));
|
||||
gx2WriteGather_submitU32AsBE(Latte::REGADDR::VGT_PRIMITIVE_TYPE - 0x2000);
|
||||
gx2WriteGather_submitU32AsBE((uint32)primitiveMode);
|
||||
// set index type
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_INDEX_TYPE, 1));
|
||||
gx2WriteGather_submitU32AsBE((uint32)indexType);
|
||||
// set number of instances
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_NUM_INSTANCES, 1));
|
||||
gx2WriteGather_submitU32AsBE((uint32)numInstances);
|
||||
// request indexed draw with indices embedded into command buffer
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_DRAW_INDEX_IMMD, 2 + numIndexU32s) | 0x00000001);
|
||||
gx2WriteGather_submitU32AsBE(count);
|
||||
gx2WriteGather_submitU32AsBE(0); // ukn
|
||||
if (use32BitIndices)
|
||||
{
|
||||
for (uint32 i = 0; i < numIndexU32s; i++)
|
||||
{
|
||||
gx2WriteGather_submitU32AsLE(indexDataU32[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint32 i = 0; i < numIndexU32s; i++)
|
||||
{
|
||||
uint32 indexPair = indexDataU32[i];
|
||||
// swap index pair
|
||||
indexPair = (indexPair >> 16) | (indexPair << 16);
|
||||
gx2WriteGather_submitU32AsLE(indexPair);
|
||||
}
|
||||
}
|
||||
|
||||
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
}
|
||||
|
||||
struct GX2DispatchComputeParam
|
||||
{
|
||||
/* +0x00 */ uint32be worksizeX;
|
||||
/* +0x04 */ uint32be worksizeY;
|
||||
/* +0x08 */ uint32be worksizeZ;
|
||||
};
|
||||
|
||||
void GX2DispatchCompute(GX2DispatchComputeParam* dispatchParam)
|
||||
{
|
||||
GX2ReserveCmdSpace(9 + 10);
|
||||
|
||||
gx2WriteGather_submit(pm4HeaderType3(IT_SET_RESOURCE, 8),
|
||||
(mmSQ_CS_DISPATCH_PARAMS - mmSQ_TEX_RESOURCE_WORD0),
|
||||
memory_virtualToPhysical(MEMPTR<GX2DispatchComputeParam>(dispatchParam).GetMPTR()),
|
||||
0xF,
|
||||
0x862000,
|
||||
1,
|
||||
0xABCD1234,
|
||||
0xABCD1234,
|
||||
0xC0000000);
|
||||
|
||||
// IT_EVENT_WRITE with RST_VTX_CNT?
|
||||
|
||||
// set primitive mode
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONFIG_REG, 2));
|
||||
gx2WriteGather_submitU32AsBE(Latte::REGADDR::VGT_PRIMITIVE_TYPE - 0x2000);
|
||||
gx2WriteGather_submitU32AsBE(1); // mode
|
||||
// set number of instances
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_NUM_INSTANCES, 1));
|
||||
gx2WriteGather_submitU32AsBE(1); // numInstances
|
||||
|
||||
uint32 workCount = (uint32)dispatchParam->worksizeX * (uint32)dispatchParam->worksizeY * (uint32)dispatchParam->worksizeZ;
|
||||
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_DRAW_INDEX_AUTO, 2) | 0x00000001);
|
||||
gx2WriteGather_submitU32AsBE(workCount);
|
||||
gx2WriteGather_submitU32AsBE(0); // DRAW_INITIATOR (has source select for index generator + other unknown info)
|
||||
}
|
||||
|
||||
void GX2DrawInit()
|
||||
{
|
||||
cafeExportRegister("gx2", GX2SetAttribBuffer, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2DrawIndexedEx, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2DrawIndexedEx2, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2DrawEx, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2DrawIndexedImmediateEx, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2DispatchCompute, LogType::GX2);
|
||||
}
|
||||
|
||||
}
|
||||
13
src/Cafe/OS/libs/gx2/GX2_Draw.h
Normal file
13
src/Cafe/OS/libs/gx2/GX2_Draw.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include "Cafe/HW/Latte/ISA/LatteReg.h"
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
using GX2IndexType = Latte::LATTE_VGT_DMA_INDEX_TYPE::E_INDEX_TYPE;
|
||||
using GX2PrimitiveMode2 = Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE;
|
||||
|
||||
void GX2SetAttribBuffer(uint32 bufferIndex, uint32 sizeInBytes, uint32 stride, void* data);
|
||||
void GX2DrawIndexedEx(GX2PrimitiveMode2 primitiveMode, uint32 count, GX2IndexType indexType, void* indexData, uint32 baseVertex, uint32 numInstances);
|
||||
|
||||
void GX2DrawInit();
|
||||
}
|
||||
311
src/Cafe/OS/libs/gx2/GX2_Event.cpp
Normal file
311
src/Cafe/OS/libs/gx2/GX2_Event.cpp
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "GX2_Command.h"
|
||||
#include "GX2_Event.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePM4.h"
|
||||
#include "Cafe/HW/MMU/MMU.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "GX2.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "util/helpers/ConcurrentQueue.h"
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
|
||||
SysAllocator<coreinit::OSThreadQueue> g_vsyncThreadQueue;
|
||||
SysAllocator<coreinit::OSThreadQueue> g_flipThreadQueue;
|
||||
|
||||
SysAllocator<coreinit::OSEvent> s_updateRetirementEvent;
|
||||
std::atomic<uint64> s_lastRetirementTimestamp = 0;
|
||||
|
||||
// called from GPU code when a command buffer is retired
|
||||
void __GX2NotifyNewRetirementTimestamp(uint64 tsRetire)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
s_lastRetirementTimestamp = tsRetire;
|
||||
coreinit::OSSignalEventAllInternal(s_updateRetirementEvent.GetPtr());
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void GX2SetGPUFence(uint32be* fencePtr, uint32 mask, uint32 compareOp, uint32 compareValue)
|
||||
{
|
||||
GX2ReserveCmdSpace(7);
|
||||
uint8 compareOpTable[] = { 0x7,0x1,0x3,0x2,0x6,0x4,0x5,0x0 };
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_WAIT_REG_MEM, 6));
|
||||
gx2WriteGather_submitU32AsBE((uint32)(compareOpTable[compareOp & 7]) | 0x10); // compare operand + memory select (0x10)
|
||||
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(fencePtr)) | 2); // physical address + type size flag(?)
|
||||
gx2WriteGather_submitU32AsBE(0); // ukn, always set to 0
|
||||
gx2WriteGather_submitU32AsBE(compareValue); // fence value
|
||||
gx2WriteGather_submitU32AsBE(mask); // fence mask
|
||||
gx2WriteGather_submitU32AsBE(0xA); // unknown purpose
|
||||
}
|
||||
|
||||
enum class GX2PipeEventType : uint32
|
||||
{
|
||||
TOP = 0,
|
||||
BOTTOM = 1,
|
||||
BOTTOM_AFTER_FLUSH = 2
|
||||
};
|
||||
|
||||
void GX2SubmitUserTimeStamp(uint64* timestampOut, uint64 value, GX2PipeEventType eventType, uint32 triggerInterrupt)
|
||||
{
|
||||
GX2ReserveCmdSpace(7);
|
||||
|
||||
MPTR physTimestampAddr = memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(timestampOut));
|
||||
uint32 valHigh = (uint32)(value >> 32);
|
||||
uint32 valLow = (uint32)(value & 0xffffffff);
|
||||
|
||||
if (eventType == GX2PipeEventType::TOP)
|
||||
{
|
||||
// write when on top of pipe
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_MEM_WRITE, 4));
|
||||
gx2WriteGather_submitU32AsBE(physTimestampAddr | 0x2);
|
||||
gx2WriteGather_submitU32AsBE(0); // 0x40000 -> 32bit write, 0x0 -> 64bit write?
|
||||
gx2WriteGather_submitU32AsBE(valLow); // low
|
||||
gx2WriteGather_submitU32AsBE(valHigh); // high
|
||||
if (triggerInterrupt != 0)
|
||||
{
|
||||
// top callback
|
||||
gx2WriteGather_submitU32AsBE(0x0000304A);
|
||||
gx2WriteGather_submitU32AsBE(0x40000000);
|
||||
}
|
||||
}
|
||||
else if (eventType == GX2PipeEventType::BOTTOM_AFTER_FLUSH)
|
||||
{
|
||||
// write when on bottom of pipe
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_MEM_WRITE, 4));
|
||||
gx2WriteGather_submitU32AsBE(physTimestampAddr | 0x2);
|
||||
gx2WriteGather_submitU32AsBE(0); // 0x40000 -> 32bit write, 0x0 -> 64bit write?
|
||||
gx2WriteGather_submitU32AsBE(valLow); // low
|
||||
gx2WriteGather_submitU32AsBE(valHigh); // high
|
||||
// trigger CB
|
||||
if (triggerInterrupt != 0)
|
||||
{
|
||||
// bottom callback
|
||||
// todo -> Fix this
|
||||
gx2WriteGather_submitU32AsBE(0x0000304B); // hax -> This event is handled differently and uses a different packet?
|
||||
gx2WriteGather_submitU32AsBE(0x40000000);
|
||||
// trigger bottom of pipe callback
|
||||
// used by Mario & Sonic Rio
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_BOTTOM_OF_PIPE_CB, 3));
|
||||
gx2WriteGather_submitU32AsBE(physTimestampAddr);
|
||||
gx2WriteGather_submitU32AsBE(valLow); // low
|
||||
gx2WriteGather_submitU32AsBE(valHigh); // high
|
||||
}
|
||||
}
|
||||
else if (eventType == GX2PipeEventType::BOTTOM)
|
||||
{
|
||||
// fix this
|
||||
// write timestamp when on bottom of pipe
|
||||
if (triggerInterrupt != 0)
|
||||
{
|
||||
// write value and trigger CB
|
||||
// todo: Use correct packet
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_BOTTOM_OF_PIPE_CB, 3));
|
||||
gx2WriteGather_submitU32AsBE(physTimestampAddr);
|
||||
gx2WriteGather_submitU32AsBE(valLow); // low
|
||||
gx2WriteGather_submitU32AsBE(valHigh); // high
|
||||
}
|
||||
else
|
||||
{
|
||||
// write value but don't trigger CB
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_MEM_WRITE, 4));
|
||||
gx2WriteGather_submitU32AsBE(physTimestampAddr | 0x2);
|
||||
gx2WriteGather_submitU32AsBE(0); // 0x40000 -> 32bit write, 0x0 -> 64bit write?
|
||||
gx2WriteGather_submitU32AsBE(valLow); // low
|
||||
gx2WriteGather_submitU32AsBE(valHigh); // high
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
}
|
||||
|
||||
struct GX2EventFunc
|
||||
{
|
||||
MEMPTR<void> callbackFuncPtr;
|
||||
MEMPTR<void> userData;
|
||||
}s_eventCallback[GX2CallbackEventTypeCount]{};
|
||||
|
||||
void GX2SetEventCallback(GX2CallbackEventType eventType, void* callbackFuncPtr, void* userData)
|
||||
{
|
||||
if ((size_t)eventType >= GX2CallbackEventTypeCount)
|
||||
{
|
||||
forceLog_printf("GX2SetEventCallback(): Unknown eventType\n");
|
||||
return;
|
||||
}
|
||||
s_eventCallback[(size_t)eventType].callbackFuncPtr = callbackFuncPtr;
|
||||
s_eventCallback[(size_t)eventType].userData = userData;
|
||||
}
|
||||
|
||||
void GX2GetEventCallback(GX2CallbackEventType eventType, MEMPTR<void>* callbackFuncPtrOut, MEMPTR<void>* userDataOut)
|
||||
{
|
||||
if ((size_t)eventType >= GX2CallbackEventTypeCount)
|
||||
{
|
||||
forceLog_printf("GX2GetEventCallback(): Unknown eventType\n");
|
||||
return;
|
||||
}
|
||||
if (callbackFuncPtrOut)
|
||||
*callbackFuncPtrOut = s_eventCallback[(size_t)eventType].callbackFuncPtr;
|
||||
if (userDataOut)
|
||||
*userDataOut = s_eventCallback[(size_t)eventType].userData;
|
||||
}
|
||||
|
||||
// event callback thread
|
||||
bool s_callbackThreadLaunched{};
|
||||
SysAllocator<OSThread_t> s_eventCallbackThread;
|
||||
SysAllocator<uint8, 0x2000> s_eventCallbackThreadStack;
|
||||
SysAllocator<char, 64> s_eventCallbackThreadName;
|
||||
// event callback queue
|
||||
struct GX2EventQueueEntry
|
||||
{
|
||||
GX2EventQueueEntry() {};
|
||||
GX2EventQueueEntry(GX2CallbackEventType eventType) : eventType(eventType) {};
|
||||
GX2CallbackEventType eventType{(GX2CallbackEventType)-1};
|
||||
};
|
||||
|
||||
SysAllocator<coreinit::OSSemaphore> s_eventCbQueueSemaphore;
|
||||
ConcurrentQueue<GX2EventQueueEntry> s_eventCbQueue;
|
||||
|
||||
void __GX2NotifyEvent(GX2CallbackEventType eventType)
|
||||
{
|
||||
if ((size_t)eventType >= GX2CallbackEventTypeCount)
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
return;
|
||||
}
|
||||
if (s_eventCallback[(size_t)eventType].callbackFuncPtr)
|
||||
{
|
||||
s_eventCbQueue.push(eventType);
|
||||
coreinit::OSSignalSemaphore(s_eventCbQueueSemaphore);
|
||||
}
|
||||
// wake up threads that are waiting for VSYNC or FLIP event
|
||||
if (eventType == GX2CallbackEventType::VSYNC)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
g_vsyncThreadQueue->wakeupEntireWaitQueue(false);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
else if (eventType == GX2CallbackEventType::FLIP)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
g_flipThreadQueue->wakeupEntireWaitQueue(false);
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
}
|
||||
|
||||
void __GX2CallbackThread(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
while (coreinit::OSWaitSemaphore(s_eventCbQueueSemaphore))
|
||||
{
|
||||
GX2EventQueueEntry entry;
|
||||
if (!s_eventCbQueue.peek2(entry))
|
||||
continue;
|
||||
if(!s_eventCallback[(size_t)entry.eventType].callbackFuncPtr)
|
||||
continue;
|
||||
PPCCoreCallback(s_eventCallback[(size_t)entry.eventType].callbackFuncPtr, (sint32)entry.eventType, s_eventCallback[(size_t)entry.eventType].userData);
|
||||
}
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
uint64 GX2GetLastSubmittedTimeStamp()
|
||||
{
|
||||
return LatteGPUState.lastSubmittedCommandBufferTimestamp.load();
|
||||
}
|
||||
|
||||
uint64 GX2GetRetiredTimeStamp()
|
||||
{
|
||||
return s_lastRetirementTimestamp;
|
||||
}
|
||||
|
||||
void GX2WaitForVsync()
|
||||
{
|
||||
__OSLockScheduler();
|
||||
g_vsyncThreadQueue.GetPtr()->queueAndWait(coreinit::OSGetCurrentThread());
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
void GX2WaitForFlip()
|
||||
{
|
||||
if ((sint32)(_swapEndianU32(LatteGPUState.sharedArea->flipRequestCountBE) == _swapEndianU32(LatteGPUState.sharedArea->flipExecuteCountBE)))
|
||||
return; // dont wait if no flip is requested
|
||||
__OSLockScheduler();
|
||||
g_flipThreadQueue.GetPtr()->queueAndWait(coreinit::OSGetCurrentThread());
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
bool GX2WaitTimeStamp(uint64 tsWait)
|
||||
{
|
||||
__OSLockScheduler();
|
||||
while (tsWait > s_lastRetirementTimestamp)
|
||||
{
|
||||
// GPU hasn't caught up yet
|
||||
coreinit::OSWaitEventInternal(s_updateRetirementEvent.GetPtr());
|
||||
}
|
||||
__OSUnlockScheduler();
|
||||
// return true to indicate no timeout
|
||||
return true;
|
||||
}
|
||||
|
||||
void GX2DrawDone()
|
||||
{
|
||||
// optional force full sync (texture readback and occlusion queries)
|
||||
bool forceFullSync = false;
|
||||
if (g_renderer && g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
forceFullSync = true;
|
||||
if (forceFullSync || ActiveSettings::WaitForGX2DrawDoneEnabled())
|
||||
{
|
||||
GX2ReserveCmdSpace(2);
|
||||
// write PM4 command
|
||||
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_SYNC_ASYNC_OPERATIONS, 1));
|
||||
gx2WriteGather_submitU32AsBE(0x00000000); // unused
|
||||
}
|
||||
// flush pipeline
|
||||
if (_GX2GetUnflushedBytes(PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance)) > 0)
|
||||
_GX2SubmitToTCL();
|
||||
|
||||
uint64 ts = GX2GetLastSubmittedTimeStamp();
|
||||
GX2WaitTimeStamp(ts);
|
||||
|
||||
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
|
||||
}
|
||||
|
||||
void GX2Init_event()
|
||||
{
|
||||
// clear queue
|
||||
|
||||
// launch event callback thread
|
||||
if (s_callbackThreadLaunched)
|
||||
return;
|
||||
s_callbackThreadLaunched = true;
|
||||
strcpy(s_eventCallbackThreadName.GetPtr(), "GX2 event callback");
|
||||
coreinit::OSCreateThreadType(s_eventCallbackThread, PPCInterpreter_makeCallableExportDepr(__GX2CallbackThread), 0, nullptr, (uint8*)s_eventCallbackThreadStack.GetPtr() + s_eventCallbackThreadStack.GetByteSize(), (sint32)s_eventCallbackThreadStack.GetByteSize(), 16, OSThread_t::ATTR_DETACHED, OSThread_t::THREAD_TYPE::TYPE_IO);
|
||||
coreinit::OSSetThreadName(s_eventCallbackThread, s_eventCallbackThreadName);
|
||||
coreinit::OSResumeThread(s_eventCallbackThread);
|
||||
}
|
||||
|
||||
void GX2EventInit()
|
||||
{
|
||||
cafeExportRegister("gx2", GX2SetGPUFence, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2SubmitUserTimeStamp, LogType::GX2);
|
||||
|
||||
cafeExportRegister("gx2", GX2SetEventCallback, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2GetEventCallback, LogType::GX2);
|
||||
|
||||
cafeExportRegister("gx2", GX2GetLastSubmittedTimeStamp, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2GetRetiredTimeStamp, LogType::GX2);
|
||||
|
||||
cafeExportRegister("gx2", GX2WaitForVsync, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2WaitForFlip, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2WaitTimeStamp, LogType::GX2);
|
||||
cafeExportRegister("gx2", GX2DrawDone, LogType::GX2);
|
||||
|
||||
coreinit::OSInitThreadQueue(g_vsyncThreadQueue.GetPtr());
|
||||
coreinit::OSInitThreadQueue(g_flipThreadQueue.GetPtr());
|
||||
|
||||
coreinit::OSInitEvent(s_updateRetirementEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO);
|
||||
coreinit::OSInitSemaphore(s_eventCbQueueSemaphore, 0);
|
||||
}
|
||||
}
|
||||
26
src/Cafe/OS/libs/gx2/GX2_Event.h
Normal file
26
src/Cafe/OS/libs/gx2/GX2_Event.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
void GX2EventInit();
|
||||
void GX2Init_event();
|
||||
|
||||
void GX2WaitForVsync();
|
||||
void GX2WaitForFlip();
|
||||
void GX2DrawDone();
|
||||
|
||||
enum class GX2CallbackEventType
|
||||
{
|
||||
TIMESTAMP_TOP = 0,
|
||||
TIMESTAMP_BOTTOM = 1,
|
||||
VSYNC = 2,
|
||||
FLIP = 3,
|
||||
// 4 is buffer overrun?
|
||||
};
|
||||
inline constexpr size_t GX2CallbackEventTypeCount = 5;
|
||||
|
||||
// notification callbacks for GPU
|
||||
void __GX2NotifyNewRetirementTimestamp(uint64 tsRetire);
|
||||
void __GX2NotifyEvent(GX2CallbackEventType eventType);
|
||||
|
||||
}
|
||||
77
src/Cafe/OS/libs/gx2/GX2_Memory.cpp
Normal file
77
src/Cafe/OS/libs/gx2/GX2_Memory.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#include "Cafe/OS/common/OSCommon.h"
|
||||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||
#include "GX2.h"
|
||||
#include "GX2_Resource.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
|
||||
// default GX2 allocator (not the same as the GX2R allocator, but GX2R uses this allocator by default)
|
||||
MPTR gx2Mem_defaultAlloc = MPTR_NULL;
|
||||
MPTR gx2Mem_defaultFree = MPTR_NULL;
|
||||
|
||||
void gx2Memory_GX2SetDefaultAllocator(MPTR defaultAllocFunc, MPTR defaulFreeFunc)
|
||||
{
|
||||
gx2Mem_defaultAlloc = defaultAllocFunc;
|
||||
gx2Mem_defaultFree = defaulFreeFunc;
|
||||
}
|
||||
|
||||
void _GX2DefaultAlloc_Alloc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// parameters:
|
||||
// r3 uint32 userParam
|
||||
// r4 uint32 size
|
||||
// r5 sint32 alignment
|
||||
hCPU->gpr[3] = hCPU->gpr[4];
|
||||
hCPU->gpr[4] = hCPU->gpr[5];
|
||||
hCPU->instructionPointer = gCoreinitData->MEMAllocFromDefaultHeapEx.GetMPTR();
|
||||
}
|
||||
|
||||
void _GX2DefaultAlloc_Free(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
hCPU->gpr[3] = hCPU->gpr[4];
|
||||
hCPU->instructionPointer = gCoreinitData->MEMFreeToDefaultHeap.GetMPTR();
|
||||
}
|
||||
|
||||
void gx2Export_GX2SetDefaultAllocator(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2SetDefaultAllocator(0x%08x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
gx2Mem_defaultAlloc = hCPU->gpr[3];
|
||||
gx2Mem_defaultFree = hCPU->gpr[4];
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void _GX2DefaultAllocR_Alloc(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2DefaultAllocate(0x%08x, 0x%08x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
||||
// parameters:
|
||||
// r3 uint32 userParam
|
||||
// r4 uint32 size
|
||||
// r5 sint32 alignment
|
||||
hCPU->instructionPointer = gx2Mem_defaultAlloc;
|
||||
}
|
||||
|
||||
void _GX2DefaultAllocR_Free(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
gx2Log_printf("GX2DefaultFree(0x%08x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4]);
|
||||
// parameters:
|
||||
// r3 uint32 userParam
|
||||
// r4 void* mem
|
||||
hCPU->instructionPointer = gx2Mem_defaultFree;
|
||||
}
|
||||
|
||||
namespace GX2
|
||||
{
|
||||
void GX2MEMAllocatorsInit()
|
||||
{
|
||||
// set default allocators (can be overwritten by GX2SetDefaultAllocator)
|
||||
gx2Mem_defaultAlloc = PPCInterpreter_makeCallableExportDepr(_GX2DefaultAlloc_Alloc);
|
||||
gx2Mem_defaultFree = PPCInterpreter_makeCallableExportDepr(_GX2DefaultAlloc_Free);
|
||||
// set resource default allocator
|
||||
GX2::GX2RSetAllocator(PPCInterpreter_makeCallableExportDepr(_GX2DefaultAllocR_Alloc), PPCInterpreter_makeCallableExportDepr(_GX2DefaultAllocR_Free));
|
||||
}
|
||||
|
||||
void GX2MemInit()
|
||||
{
|
||||
osLib_addFunction("gx2", "GX2SetDefaultAllocator", gx2Export_GX2SetDefaultAllocator);
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue