mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-17 07:37:02 +00:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
129
src/Cafe/OS/RPL/elf.cpp
Normal file
129
src/Cafe/OS/RPL/elf.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#include <zlib.h>
|
||||
#include "Cafe/OS/RPL/rpl.h"
|
||||
#include "Cafe/OS/RPL/rpl_structs.h"
|
||||
#include "util/VirtualHeap/VirtualHeap.h"
|
||||
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
|
||||
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32be magic;
|
||||
/* +0x04 */ uint8 eiClass;
|
||||
/* +0x05 */ uint8 eiData;
|
||||
/* +0x06 */ uint8 eiVersion;
|
||||
/* +0x07 */ uint8 eiOSABI;
|
||||
/* +0x08 */ uint8 eiOSABIVersion;
|
||||
/* +0x09 */ uint8 eiPadding[7];
|
||||
/* +0x10 */ uint16be eType;
|
||||
/* +0x12 */ uint16be eMachine;
|
||||
/* +0x14 */ uint32be eVersion;
|
||||
/* +0x18 */ uint32be entrypoint;
|
||||
/* +0x1C */ uint32be phOffset;
|
||||
/* +0x20 */ uint32be shOffset;
|
||||
|
||||
/* +0x24 */ uint32be eFlags;
|
||||
/* +0x28 */ uint16be eHeaderSize;
|
||||
/* +0x2A */ uint16be ePHEntrySize;
|
||||
/* +0x2C */ uint16be ePHNum;
|
||||
/* +0x2E */ uint16be eSHEntrySize;
|
||||
/* +0x30 */ uint16be eSHNum;
|
||||
/* +0x32 */ uint16be eShStrIndex;
|
||||
}elfHeader_t;
|
||||
|
||||
static_assert(sizeof(elfHeader_t) == 0x34, "");
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32be nameOffset;
|
||||
/* +0x04 */ uint32be shType;
|
||||
/* +0x08 */ uint32be shFlags;
|
||||
/* +0x0C */ uint32be shAddr;
|
||||
/* +0x10 */ uint32be shOffset;
|
||||
/* +0x14 */ uint32be shSize;
|
||||
|
||||
/* +0x18 */ uint32be shLink;
|
||||
/* +0x1C */ uint32be shInfo;
|
||||
|
||||
/* +0x20 */ uint32be shAddrAlign;
|
||||
/* +0x24 */ uint32be shEntSize;
|
||||
}elfSectionEntry_t;
|
||||
|
||||
static_assert(sizeof(elfSectionEntry_t) == 0x28, "");
|
||||
|
||||
// Map elf into memory
|
||||
uint32 ELF_LoadFromMemory(uint8* elfData, sint32 size, const char* name)
|
||||
{
|
||||
elfHeader_t* header = (elfHeader_t*)elfData;
|
||||
|
||||
uint32 sectionCount = header->eSHNum;
|
||||
uint32 sectionTableOffset = header->shOffset;
|
||||
uint32 sectionTableEntrySize = header->eSHEntrySize;
|
||||
|
||||
elfSectionEntry_t* sectionTable = (elfSectionEntry_t*)(elfData + (uint32)header->shOffset);
|
||||
memory_enableHBLELFCodeArea();
|
||||
for (uint32 i = 0; i < sectionCount; i++)
|
||||
{
|
||||
debug_printf("%02d Addr %08x Size %08x Offs %08x Flags %08x Type %08x EntSize %08x\n", i, (uint32)sectionTable[i].shAddr, (uint32)sectionTable[i].shSize, (uint32)sectionTable[i].shOffset, (uint32)sectionTable[i].shFlags, (uint32)sectionTable[i].shType, (uint32)sectionTable[i].shEntSize);
|
||||
uint32 shAddr = (uint32)sectionTable[i].shAddr;
|
||||
uint32 shSize = (uint32)sectionTable[i].shSize;
|
||||
uint32 shOffset = (uint32)sectionTable[i].shOffset;
|
||||
uint32 shType = (uint32)sectionTable[i].shType;
|
||||
|
||||
if (shOffset > (uint32)size)
|
||||
{
|
||||
forceLog_printf("ELF section %d out of bounds", i);
|
||||
continue;
|
||||
}
|
||||
uint32 copySize = std::min(shSize, size - shOffset);
|
||||
|
||||
// 0x00802000
|
||||
if (shAddr != 0)
|
||||
{
|
||||
if (shType == SHT_NOBITS)
|
||||
{
|
||||
memset(memory_getPointerFromVirtualOffset(shAddr), 0, shSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(memory_getPointerFromVirtualOffset(shAddr), elfData + shOffset, copySize);
|
||||
}
|
||||
// SHT_NOBITS
|
||||
}
|
||||
}
|
||||
return header->entrypoint;
|
||||
}
|
||||
|
||||
// From Homebrew Launcher:
|
||||
//#define MEM_BASE (0x00800000)
|
||||
//#define ELF_DATA_ADDR (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x00))
|
||||
//#define ELF_DATA_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x04))
|
||||
//#define HBL_CHANNEL (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x08))
|
||||
//#define RPX_MAX_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x0C))
|
||||
//#define RPX_MAX_CODE_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x10))
|
||||
//#define MAIN_ENTRY_ADDR (*(volatile unsigned int*)(MEM_BASE + 0x1400 + 0x00))
|
||||
//#define OS_FIRMWARE (*(volatile unsigned int*)(MEM_BASE + 0x1400 + 0x04))
|
||||
//
|
||||
//#define OS_SPECIFICS ((OsSpecifics*)(MEM_BASE + 0x1500))
|
||||
//
|
||||
//#define MEM_AREA_TABLE ((s_mem_area*)(MEM_BASE + 0x1600))
|
||||
|
||||
//typedef struct _OsSpecifics
|
||||
//{
|
||||
// unsigned int addr_OSDynLoad_Acquire;
|
||||
// unsigned int addr_OSDynLoad_FindExport;
|
||||
// unsigned int addr_OSTitle_main_entry;
|
||||
//
|
||||
// unsigned int addr_KernSyscallTbl1;
|
||||
// unsigned int addr_KernSyscallTbl2;
|
||||
// unsigned int addr_KernSyscallTbl3;
|
||||
// unsigned int addr_KernSyscallTbl4;
|
||||
// unsigned int addr_KernSyscallTbl5;
|
||||
//
|
||||
// int(*LiWaitIopComplete)(int, int *);
|
||||
// int(*LiWaitIopCompleteWithInterrupts)(int, int *);
|
||||
// unsigned int addr_LiWaitOneChunk;
|
||||
// unsigned int addr_PrepareTitle_hook;
|
||||
// unsigned int addr_sgIsLoadingBuffer;
|
||||
// unsigned int addr_gDynloadInitialized;
|
||||
// unsigned int orig_LiWaitOneChunkInstr;
|
||||
//} OsSpecifics;
|
||||
2389
src/Cafe/OS/RPL/rpl.cpp
Normal file
2389
src/Cafe/OS/RPL/rpl.cpp
Normal file
File diff suppressed because it is too large
Load diff
55
src/Cafe/OS/RPL/rpl.h
Normal file
55
src/Cafe/OS/RPL/rpl.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
struct RPLModule;
|
||||
|
||||
#define RPL_INVALID_HANDLE (0xFFFFFFFF)
|
||||
|
||||
void RPLLoader_InitState();
|
||||
void RPLLoader_ResetState();
|
||||
|
||||
uint8* RPLLoader_AllocateTrampolineCodeSpace(sint32 size);
|
||||
|
||||
MPTR RPLLoader_AllocateCodeSpace(uint32 size, uint32 alignment);
|
||||
|
||||
uint32 RPLLoader_GetMaxCodeOffset();
|
||||
uint32 RPLLoader_GetDataAllocatorAddr();
|
||||
|
||||
__declspec(dllexport) RPLModule* rpl_loadFromMem(uint8* rplData, sint32 size, char* name);
|
||||
uint32 rpl_mapHLEImport(RPLModule* rplLoaderContext, const char* rplName, const char* funcName, bool functionMustExist);
|
||||
void RPLLoader_Link();
|
||||
|
||||
MPTR RPLLoader_FindRPLExport(RPLModule* rplLoaderContext, const char* symbolName, bool isData);
|
||||
uint32 RPLLoader_GetModuleEntrypoint(RPLModule* rplLoaderContext);
|
||||
|
||||
void RPLLoader_SetMainModule(RPLModule* rplLoaderContext);
|
||||
uint32 RPLLoader_GetMainModuleHandle();
|
||||
|
||||
void RPLLoader_CallEntrypoints();
|
||||
void RPLLoader_NotifyControlPassedToApplication();
|
||||
|
||||
void RPLLoader_AddDependency(const char* name);
|
||||
void RPLLoader_RemoveDependency(uint32 handle);
|
||||
void RPLLoader_UpdateDependencies();
|
||||
|
||||
uint32 RPLLoader_GetHandleByModuleName(const char* name);
|
||||
uint32 RPLLoader_GetMaxTLSModuleIndex();
|
||||
bool RPLLoader_GetTLSDataByTLSIndex(sint16 tlsModuleIndex, uint8** tlsData, sint32* tlsSize);
|
||||
|
||||
uint32 RPLLoader_FindModuleOrHLEExport(uint32 moduleHandle, bool isData, const char* exportName);
|
||||
|
||||
uint32 RPLLoader_GetSDA1Base();
|
||||
uint32 RPLLoader_GetSDA2Base();
|
||||
|
||||
sint32 RPLLoader_GetModuleCount();
|
||||
RPLModule** RPLLoader_GetModuleList();
|
||||
|
||||
MEMPTR<void> RPLLoader_AllocateCodeCaveMem(uint32 alignment, uint32 size);
|
||||
void RPLLoader_ReleaseCodeCaveMem(MEMPTR<void> addr);
|
||||
|
||||
// exports
|
||||
|
||||
uint32 RPLLoader_MakePPCCallable(void(*ppcCallableExport)(struct PPCInterpreter_t* hCPU));
|
||||
|
||||
// elf loader
|
||||
|
||||
uint32 ELF_LoadFromMemory(uint8* elfData, sint32 size, const char* name);
|
||||
21
src/Cafe/OS/RPL/rpl_debug_symbols.cpp
Normal file
21
src/Cafe/OS/RPL/rpl_debug_symbols.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#include "Cafe/OS/RPL/rpl_debug_symbols.h"
|
||||
|
||||
std::map<MPTR, rplDebugSymbolBase*> map_DebugSymbols;
|
||||
|
||||
void rplDebugSymbol_createComment(MPTR address, const wchar_t* comment)
|
||||
{
|
||||
auto new_comment = new rplDebugSymbolComment();
|
||||
new_comment->type = RplDebugSymbolComment;
|
||||
new_comment->comment = comment;
|
||||
map_DebugSymbols[address] = new_comment;
|
||||
}
|
||||
|
||||
rplDebugSymbolBase* rplDebugSymbol_getForAddress(MPTR address)
|
||||
{
|
||||
return map_DebugSymbols[address];
|
||||
}
|
||||
|
||||
const std::map<MPTR, rplDebugSymbolBase*>& rplDebugSymbol_getSymbols()
|
||||
{
|
||||
return map_DebugSymbols;
|
||||
}
|
||||
23
src/Cafe/OS/RPL/rpl_debug_symbols.h
Normal file
23
src/Cafe/OS/RPL/rpl_debug_symbols.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include <map>
|
||||
|
||||
enum RplDebugSymbolType : uint8
|
||||
{
|
||||
RplDebugSymbolComment = 0,
|
||||
};
|
||||
|
||||
struct rplDebugSymbolBase
|
||||
{
|
||||
RplDebugSymbolType type;
|
||||
rplDebugSymbolBase* next;
|
||||
};
|
||||
|
||||
struct rplDebugSymbolComment : rplDebugSymbolBase
|
||||
{
|
||||
std::wstring comment;
|
||||
};
|
||||
|
||||
|
||||
void rplDebugSymbol_createComment(MPTR address, const wchar_t* comment);
|
||||
rplDebugSymbolBase* rplDebugSymbol_getForAddress(MPTR address);
|
||||
const std::map<MPTR, rplDebugSymbolBase*>& rplDebugSymbol_getSymbols();
|
||||
258
src/Cafe/OS/RPL/rpl_structs.h
Normal file
258
src/Cafe/OS/RPL/rpl_structs.h
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/ChunkedHeap/ChunkedHeap.h"
|
||||
|
||||
#define RPL_MODULE_NAME_LENGTH 64
|
||||
#define RPL_MODULE_PATH_LENGTH 256
|
||||
|
||||
// types
|
||||
#define SHT_RPL_EXPORTS (0x80000001)
|
||||
#define SHT_RPL_IMPORTS (0x80000002)
|
||||
#define SHT_RPL_CRCS (0x80000003)
|
||||
#define SHT_RPL_FILEINFO (0x80000004)
|
||||
|
||||
#define SHT_PROGBITS (0x00000001)
|
||||
#define SHT_SYMTAB (0x00000002)
|
||||
#define SHT_STRTAB (0x00000003)
|
||||
#define SHT_RELA (0x00000004)
|
||||
#define SHT_HASH (0x00000005)
|
||||
#define SHT_DYNAMIC (0x00000006)
|
||||
#define SHT_NOTE (0x00000007)
|
||||
#define SHT_NOBITS (0x00000008) // this section contains no data
|
||||
#define SHT_REL (0x00000009)
|
||||
#define SHT_SHLIB (0x0000000A)
|
||||
#define SHT_DYNSYM (0x0000000B)
|
||||
|
||||
// flags
|
||||
#define SHF_EXECUTE 0x00000004
|
||||
#define SHF_RPL_COMPRESSED 0x08000000
|
||||
#define SHF_TLS 0x04000000
|
||||
|
||||
// relocs
|
||||
#define RPL_RELOC_ADDR32 1
|
||||
#define RPL_RELOC_LO16 4
|
||||
#define RPL_RELOC_HI16 5
|
||||
#define RPL_RELOC_HA16 6
|
||||
#define RPL_RELOC_REL24 10
|
||||
#define RPL_RELOC_REL14 11
|
||||
|
||||
#define R_PPC_DTPMOD32 68
|
||||
#define R_PPC_DTPREL32 78
|
||||
|
||||
#define R_PPC_REL16_HA 251
|
||||
#define R_PPC_REL16_HI 252
|
||||
#define R_PPC_REL16_LO 253
|
||||
|
||||
#define HLE_MODULE_PTR ((RPLModule*)-1)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32be relocOffset;
|
||||
/* +0x04 */ uint32be symbolIndexAndType;
|
||||
/* +0x08 */ uint32be relocAddend;
|
||||
}rplRelocNew_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32be nameOffset;
|
||||
/* +0x04 */ uint32be type;
|
||||
/* +0x08 */ uint32be flags;
|
||||
/* +0x0C */ uint32be virtualAddress;
|
||||
/* +0x10 */ uint32be fileOffset;
|
||||
/* +0x14 */ uint32be sectionSize;
|
||||
/* +0x18 */ uint32be symtabSectionIndex;
|
||||
/* +0x1C */ uint32be relocTargetSectionIndex;
|
||||
/* +0x20 */ uint32be alignment;
|
||||
/* +0x24 */ uint32be ukn24; // for symtab: Size of each symbol entry
|
||||
}rplSectionEntryNew_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32be magic1;
|
||||
/* +0x04 */ uint8 version04; // probably version?
|
||||
/* +0x05 */ uint8 ukn05; // probably version?
|
||||
/* +0x06 */ uint8 ukn06; // probably version?
|
||||
/* +0x07 */ uint8 magic2_0; // part of second magic
|
||||
/* +0x08 */ uint8 magic2_1; // part of second magic
|
||||
/* +0x09 */ uint8 ukn09;
|
||||
/* +0x0A */ uint8 ukn0A;
|
||||
/* +0x0B */ uint8 ukn0B;
|
||||
/* +0x0C */ uint32be dataRegionSize;
|
||||
/* +0x10 */ uint8 ukn10;
|
||||
/* +0x11 */ uint8 ukn11;
|
||||
/* +0x12 */ uint16be ukn12;
|
||||
/* +0x14 */ uint32be ukn14;
|
||||
/* +0x18 */ uint32be entrypoint;
|
||||
/* +0x1C */ uint32be ukn1C;
|
||||
/* +0x20 */ uint32be sectionTableOffset;
|
||||
/* +0x24 */ uint32be ukn24;
|
||||
/* +0x28 */ uint16be ukn28;
|
||||
/* +0x2A */ uint16be programHeaderTableEntrySize;
|
||||
/* +0x2C */ uint16be programHeaderTableEntryCount;
|
||||
/* +0x2E */ uint16be sectionTableEntrySize;
|
||||
/* +0x30 */ uint16be sectionTableEntryCount;
|
||||
/* +0x32 */ uint16be nameSectionIndex;
|
||||
}rplHeaderNew_t;
|
||||
|
||||
static_assert(offsetof(rplHeaderNew_t, dataRegionSize) == 0xC);
|
||||
static_assert(offsetof(rplHeaderNew_t, programHeaderTableEntrySize) == 0x2A);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* +0x00 */ uint32be fileInfoMagic; // always 0xCAFE0402
|
||||
/* +0x04 */ uint32be textRegionSize; // text region size
|
||||
/* +0x08 */ uint32be ukn08; // base align text
|
||||
/* +0x0C */ uint32be dataRegionSize; // size of data sections
|
||||
/* +0x10 */ uint32be baseAlign; // base align data?
|
||||
/* +0x14 */ uint32be ukn14;
|
||||
/* +0x18 */ uint32be ukn18;
|
||||
/* +0x1C */ uint32be ukn1C;
|
||||
/* +0x20 */ uint32be trampolineAdjustment;
|
||||
/* +0x24 */ uint32be sdataBase1;
|
||||
/* +0x28 */ uint32be sdataBase2;
|
||||
/* +0x2C */ uint32be ukn2C;
|
||||
/* +0x30 */ uint32be ukn30;
|
||||
/* +0x34 */ uint32be ukn34;
|
||||
/* +0x38 */ uint32be ukn38;
|
||||
/* +0x3C */ uint32be ukn3C;
|
||||
/* +0x40 */ uint32be toolkitVersion;
|
||||
/* +0x44 */ uint32be ukn44;
|
||||
/* +0x48 */ uint32be ukn48;
|
||||
/* +0x4C */ uint32be ukn4C;
|
||||
/* +0x50 */ uint32be ukn50;
|
||||
/* +0x54 */ uint32be ukn54;
|
||||
/* +0x58 */ sint16be tlsModuleIndex;
|
||||
}RPLFileInfoData;
|
||||
|
||||
static_assert(offsetof(RPLFileInfoData, tlsModuleIndex) == 0x58);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//uint32 address;
|
||||
void* ptr;
|
||||
}rplSectionAddressEntry_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32be virtualOffset;
|
||||
uint32be nameOffset;
|
||||
}rplExportTableEntry_t;
|
||||
|
||||
struct RPLModule
|
||||
{
|
||||
uint32 ukn00; // pointer to shared memory region? (0xEFE01000)
|
||||
uint32 ukn04; // related to text region size?
|
||||
char* moduleNamePtr__depr; // converted to lower case
|
||||
uint32 moduleNameLength__depr; // length of module name
|
||||
uint32 moduleNameSize; // aligned alloc size, not the same as actual length
|
||||
uint32 padding14;
|
||||
uint32 padding18;
|
||||
rplHeaderNew_t rplHeader;
|
||||
rplSectionEntryNew_t* sectionTablePtr; // copy of section table
|
||||
|
||||
RPLFileInfoData* fileInfoPtr__depr{}; // copy of fileinfo section
|
||||
uint32 fileInfoSize__depr{}; // size of fileInfo section
|
||||
uint32 fileInfoAllocSize__depr{}; // aligned alloc size
|
||||
|
||||
uint32be* crcTablePtr_depr{}; // copy of CRC section
|
||||
uint32 crcTableAllocSize_depr{};
|
||||
|
||||
uint32 entrypoint;
|
||||
|
||||
uint8* rplData_depr; // Cemuhook might still read this
|
||||
|
||||
MPTR textRegionTemp; // temporary memory for text section?
|
||||
|
||||
MEMPTR<void> regionMappingBase_text; // base destination address for text region
|
||||
MPTR regionMappingBase_data; // base destination address for data region
|
||||
MPTR regionMappingBase_loaderInfo; // base destination address for loaderInfo region
|
||||
uint8* tempRegionPtr;
|
||||
uint32 tempRegionAllocSize;
|
||||
|
||||
rplSectionAddressEntry_t* sectionAddressTable__depr;
|
||||
uint32 sectionAddressTableSize__depr;
|
||||
|
||||
uint32 exportDCount;
|
||||
rplExportTableEntry_t* exportDDataPtr;
|
||||
uint32 exportFCount;
|
||||
rplExportTableEntry_t* exportFDataPtr;
|
||||
|
||||
/* above are hardcoded in Cemuhook */
|
||||
std::string moduleName2;
|
||||
|
||||
std::vector<rplSectionAddressEntry_t> sectionAddressTable2;
|
||||
|
||||
uint32 tlsStartAddress;
|
||||
uint32 tlsEndAddress;
|
||||
uint32 regionSize_text;
|
||||
uint32 regionSize_data;
|
||||
uint32 regionSize_loaderInfo;
|
||||
|
||||
uint32 patchCRC; // Cemuhook style module crc for patches.txt
|
||||
|
||||
// trampoline management
|
||||
ChunkedFlatAllocator<16 * 1024> heapTrampolineArea;
|
||||
std::unordered_map<MPTR, MPTR> trampolineMap;
|
||||
|
||||
// section data
|
||||
std::vector<uint8> sectionData_fileInfo;
|
||||
std::vector<uint8> sectionData_crc;
|
||||
|
||||
// parsed FILEINFO
|
||||
struct
|
||||
{
|
||||
uint32 textRegionSize; // size of region containing all text sections
|
||||
//uint32 ukn08; // base align text?
|
||||
uint32 dataRegionSize; // size of region containing all data sections
|
||||
uint32 baseAlign;
|
||||
uint32 ukn14;
|
||||
uint32 trampolineAdjustment;
|
||||
uint32 ukn4C;
|
||||
sint16 tlsModuleIndex;
|
||||
|
||||
uint32 sdataBase1;
|
||||
uint32 sdataBase2;
|
||||
}fileInfo;
|
||||
// parsed CRC
|
||||
std::vector<uint32> crcTable;
|
||||
|
||||
uint32 GetSectionCRC(size_t sectionIndex) const
|
||||
{
|
||||
if (sectionIndex >= crcTable.size())
|
||||
return 0;
|
||||
return crcTable[sectionIndex];
|
||||
}
|
||||
|
||||
// state
|
||||
bool isLinked; // set to true if _linkModule was called on this module
|
||||
bool entrypointCalled; // set if entrypoint was called
|
||||
|
||||
// allocator
|
||||
betype<MPTR> funcAlloc;
|
||||
betype<MPTR> funcFree;
|
||||
|
||||
// replaces rplData ptr
|
||||
std::span<uint8> RPLRawData;
|
||||
|
||||
bool debugSectionLoadMask[128] = { false };
|
||||
bool hasError{ false };
|
||||
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char modulename[RPL_MODULE_NAME_LENGTH];
|
||||
char filepath[RPL_MODULE_PATH_LENGTH];
|
||||
bool loadAttempted;
|
||||
//bool isHLEModule; // determined to be a HLE module
|
||||
RPLModule* rplLoaderContext; // context of loaded module
|
||||
sint32 referenceCount;
|
||||
uint32 coreinitHandle; // fake handle for coreinit
|
||||
sint16 tlsModuleIndex; // tls module index assigned to this dependency
|
||||
}rplDependency_t;
|
||||
|
||||
RPLModule* RPLLoader_FindModuleByCodeAddr(uint32 addr);
|
||||
RPLModule* RPLLoader_FindModuleByDataAddr(uint32 addr);
|
||||
RPLModule* RPLLoader_FindModuleByName(std::string module);
|
||||
150
src/Cafe/OS/RPL/rpl_symbol_storage.cpp
Normal file
150
src/Cafe/OS/RPL/rpl_symbol_storage.cpp
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
#include "Cafe/OS/RPL/rpl.h"
|
||||
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
|
||||
|
||||
struct rplSymbolLib_t
|
||||
{
|
||||
char* libName;
|
||||
rplSymbolLib_t* next;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
rplSymbolLib_t* libs;
|
||||
std::mutex m_symbolStorageMutex;
|
||||
std::unordered_map<uint32, RPLStoredSymbol*> map_symbolByAddress;
|
||||
// allocator for strings
|
||||
char* strAllocatorBlock;
|
||||
sint32 strAllocatorOffset;
|
||||
std::vector<void*> list_strAllocatedBlocks;
|
||||
}rplSymbolStorage = { 0 };
|
||||
|
||||
#define STR_ALLOC_BLOCK_SIZE (128*1024) // allocate 128KB blocks at once
|
||||
|
||||
char* rplSymbolStorage_allocDupString(const char* str)
|
||||
{
|
||||
sint32 len = (sint32)strlen(str);
|
||||
if (rplSymbolStorage.strAllocatorBlock == nullptr || (rplSymbolStorage.strAllocatorOffset + len + 1) >= STR_ALLOC_BLOCK_SIZE)
|
||||
{
|
||||
// allocate new block
|
||||
rplSymbolStorage.strAllocatorBlock = (char*)malloc(STR_ALLOC_BLOCK_SIZE);
|
||||
rplSymbolStorage.strAllocatorOffset = 0;
|
||||
rplSymbolStorage.list_strAllocatedBlocks.emplace_back(rplSymbolStorage.strAllocatorBlock);
|
||||
}
|
||||
cemu_assert_debug((rplSymbolStorage.strAllocatorOffset + len + 1) <= STR_ALLOC_BLOCK_SIZE);
|
||||
char* allocatedStr = rplSymbolStorage.strAllocatorBlock + rplSymbolStorage.strAllocatorOffset;
|
||||
rplSymbolStorage.strAllocatorOffset += len + 1;
|
||||
strcpy(allocatedStr, str);
|
||||
return allocatedStr;
|
||||
}
|
||||
|
||||
char* rplSymbolStorage_storeLibname(const char* libName)
|
||||
{
|
||||
if (rplSymbolStorage.libs == NULL)
|
||||
{
|
||||
rplSymbolLib_t* libEntry = new rplSymbolLib_t();
|
||||
libEntry->libName = rplSymbolStorage_allocDupString(libName);
|
||||
libEntry->next = NULL;
|
||||
rplSymbolStorage.libs = libEntry;
|
||||
return libEntry->libName;
|
||||
}
|
||||
rplSymbolLib_t* libItr = rplSymbolStorage.libs;
|
||||
while (libItr)
|
||||
{
|
||||
if (boost::iequals(libItr->libName, libName))
|
||||
return libItr->libName;
|
||||
// next
|
||||
libItr = libItr->next;
|
||||
}
|
||||
// create new entry
|
||||
rplSymbolLib_t* libEntry = new rplSymbolLib_t();
|
||||
libEntry->libName = rplSymbolStorage_allocDupString(libName);
|
||||
libEntry->next = rplSymbolStorage.libs;
|
||||
rplSymbolStorage.libs = libEntry;
|
||||
return libEntry->libName;
|
||||
}
|
||||
|
||||
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
|
||||
char* libNameStorage = rplSymbolStorage_storeLibname(libName);
|
||||
char* symbolNameStorage = rplSymbolStorage_allocDupString(symbolName);
|
||||
RPLStoredSymbol* storedSymbol = new RPLStoredSymbol();
|
||||
storedSymbol->address = address;
|
||||
storedSymbol->libName = libNameStorage;
|
||||
storedSymbol->symbolName = symbolNameStorage;
|
||||
storedSymbol->flags = 0;
|
||||
rplSymbolStorage.map_symbolByAddress[address] = storedSymbol;
|
||||
return storedSymbol;
|
||||
}
|
||||
|
||||
RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
|
||||
return rplSymbolStorage.map_symbolByAddress[address];
|
||||
}
|
||||
|
||||
void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
|
||||
if (rplSymbolStorage.map_symbolByAddress[storedSymbol->address] == storedSymbol)
|
||||
rplSymbolStorage.map_symbolByAddress[storedSymbol->address] = nullptr;
|
||||
delete storedSymbol;
|
||||
}
|
||||
|
||||
void rplSymbolStorage_removeRange(MPTR address, sint32 length)
|
||||
{
|
||||
while (length > 0)
|
||||
{
|
||||
RPLStoredSymbol* symbol = rplSymbolStorage_getByAddress(address);
|
||||
if (symbol)
|
||||
rplSymbolStorage_remove(symbol);
|
||||
address += 4;
|
||||
length -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
void rplSymbolStorage_createJumpProxySymbol(MPTR jumpAddress, MPTR destAddress)
|
||||
{
|
||||
RPLStoredSymbol* destSymbol = rplSymbolStorage_getByAddress(destAddress);
|
||||
if (destSymbol)
|
||||
rplSymbolStorage_store((char*)destSymbol->libName, (char*)destSymbol->symbolName, jumpAddress);
|
||||
}
|
||||
|
||||
std::unordered_map<uint32, RPLStoredSymbol*>& rplSymbolStorage_lockSymbolMap()
|
||||
{
|
||||
rplSymbolStorage.m_symbolStorageMutex.lock();
|
||||
return rplSymbolStorage.map_symbolByAddress;
|
||||
}
|
||||
|
||||
void rplSymbolStorage_unlockSymbolMap()
|
||||
{
|
||||
rplSymbolStorage.m_symbolStorageMutex.unlock();
|
||||
}
|
||||
|
||||
void rplSymbolStorage_init()
|
||||
{
|
||||
cemu_assert_debug(rplSymbolStorage.map_symbolByAddress.empty());
|
||||
cemu_assert_debug(rplSymbolStorage.strAllocatorBlock == nullptr);
|
||||
}
|
||||
|
||||
void rplSymbolStorage_unloadAll()
|
||||
{
|
||||
// free symbols
|
||||
for (auto& it : rplSymbolStorage.map_symbolByAddress)
|
||||
delete it.second;
|
||||
rplSymbolStorage.map_symbolByAddress.clear();
|
||||
// free libs
|
||||
rplSymbolLib_t* lib = rplSymbolStorage.libs;
|
||||
while (lib)
|
||||
{
|
||||
rplSymbolLib_t* next = lib->next;
|
||||
delete lib;
|
||||
lib = next;
|
||||
}
|
||||
rplSymbolStorage.libs = nullptr;
|
||||
// free strings
|
||||
for (auto it : rplSymbolStorage.list_strAllocatedBlocks)
|
||||
free(it);
|
||||
rplSymbolStorage.strAllocatorBlock = nullptr;
|
||||
rplSymbolStorage.strAllocatorOffset = 0;
|
||||
}
|
||||
18
src/Cafe/OS/RPL/rpl_symbol_storage.h
Normal file
18
src/Cafe/OS/RPL/rpl_symbol_storage.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
struct RPLStoredSymbol
|
||||
{
|
||||
MPTR address;
|
||||
void* libName;
|
||||
void* symbolName;
|
||||
uint32 flags;
|
||||
};
|
||||
|
||||
void rplSymbolStorage_init();
|
||||
void rplSymbolStorage_unloadAll();
|
||||
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address);
|
||||
void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol);
|
||||
void rplSymbolStorage_removeRange(MPTR address, sint32 length);
|
||||
RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address);
|
||||
void rplSymbolStorage_createJumpProxySymbol(MPTR jumpAddress, MPTR destAddress);
|
||||
|
||||
std::unordered_map<uint32, RPLStoredSymbol*>& rplSymbolStorage_lockSymbolMap();
|
||||
void rplSymbolStorage_unlockSymbolMap();
|
||||
220
src/Cafe/OS/common/OSCommon.cpp
Normal file
220
src/Cafe/OS/common/OSCommon.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
#include "Cafe/HW/Espresso/PPCState.h"
|
||||
#include "Cafe/OS/libs/proc_ui/proc_ui.h"
|
||||
#include "Cafe/OS/libs/nsysnet/nsysnet.h"
|
||||
#include "Cafe/OS/libs/nlibnss/nlibnss.h"
|
||||
#include "Cafe/OS/libs/nlibcurl/nlibcurl.h"
|
||||
#include "Cafe/OS/libs/nn_nfp/nn_nfp.h"
|
||||
#include "Cafe/OS/libs/nn_act/nn_act.h"
|
||||
#include "Cafe/OS/libs/nn_acp/nn_acp.h"
|
||||
#include "Cafe/OS/libs/nn_ac/nn_ac.h"
|
||||
#include "Cafe/OS/libs/nn_uds/nn_uds.h"
|
||||
#include "Cafe/OS/libs/nn_nim/nn_nim.h"
|
||||
#include "Cafe/OS/libs/nn_ndm/nn_ndm.h"
|
||||
#include "Cafe/OS/libs/nn_ec/nn_ec.h"
|
||||
#include "Cafe/OS/libs/nn_boss/nn_boss.h"
|
||||
#include "Cafe/OS/libs/nn_fp/nn_fp.h"
|
||||
#include "Cafe/OS/libs/nn_olv/nn_olv.h"
|
||||
#include "Cafe/OS/libs/nn_idbe/nn_idbe.h"
|
||||
#include "Cafe/OS/libs/nn_save/nn_save.h"
|
||||
#include "Cafe/OS/libs/erreula/erreula.h"
|
||||
#include "Cafe/OS/libs/sysapp/sysapp.h"
|
||||
#include "Cafe/OS/libs/dmae/dmae.h"
|
||||
#include "Cafe/OS/libs/snd_core/ax.h"
|
||||
#include "Cafe/OS/libs/gx2/GX2.h"
|
||||
#include "Cafe/OS/libs/vpad/vpad.h"
|
||||
#include "Cafe/OS/libs/nsyskbd/nsyskbd.h"
|
||||
#include "Cafe/OS/libs/nsyshid/nsyshid.h"
|
||||
#include "Cafe/OS/libs/snd_user/snd_user.h"
|
||||
#include "Cafe/OS/libs/zlib125/zlib125.h"
|
||||
#include "Cafe/OS/libs/padscore/padscore.h"
|
||||
#include "Cafe/OS/libs/camera/camera.h"
|
||||
#include "../libs/swkbd/swkbd.h"
|
||||
|
||||
struct osFunctionEntry_t
|
||||
{
|
||||
uint32 libHashA;
|
||||
uint32 libHashB;
|
||||
uint32 funcHashA;
|
||||
uint32 funcHashB;
|
||||
std::string name;
|
||||
HLEIDX hleFunc;
|
||||
|
||||
osFunctionEntry_t(uint32 libHashA, uint32 libHashB, uint32 funcHashA, uint32 funcHashB, std::string_view name, HLEIDX hleFunc) :
|
||||
libHashA(libHashA), libHashB(libHashB), funcHashA(funcHashA), funcHashB(funcHashB), name(name), hleFunc(hleFunc) {};
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32 libHashA;
|
||||
uint32 libHashB;
|
||||
uint32 funcHashA;
|
||||
uint32 funcHashB;
|
||||
uint32 vPtr;
|
||||
}osPointerEntry_t;
|
||||
|
||||
std::vector<osFunctionEntry_t>* s_osFunctionTable;
|
||||
std::vector<osPointerEntry_t> osDataTable;
|
||||
|
||||
void osLib_generateHashFromName(const char* name, uint32* hashA, uint32* hashB)
|
||||
{
|
||||
uint32 h1 = 0x688BA2BA;
|
||||
uint32 h2 = 0xF64A71D5;
|
||||
while( *name )
|
||||
{
|
||||
uint32 c = (uint32)*name;
|
||||
h1 += c;
|
||||
h1 = (h1<<3)|((h1>>29));
|
||||
h2 ^= c;
|
||||
h2 = (h2<<7)|((h2>>25));
|
||||
h1 += h2;
|
||||
h2 += c;
|
||||
h2 = (h2<<3)|((h2>>29));
|
||||
name++;
|
||||
}
|
||||
*hashA = h1;
|
||||
*hashB = h2;
|
||||
}
|
||||
|
||||
void osLib_addFunctionInternal(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t* hCPU))
|
||||
{
|
||||
if (!s_osFunctionTable)
|
||||
s_osFunctionTable = new std::vector<osFunctionEntry_t>(); // replace with static allocation + constinit once we have C++20 available
|
||||
// calculate hash
|
||||
uint32 libHashA, libHashB;
|
||||
uint32 funcHashA, funcHashB;
|
||||
osLib_generateHashFromName(libraryName, &libHashA, &libHashB);
|
||||
osLib_generateHashFromName(functionName, &funcHashA, &funcHashB);
|
||||
// if entry already exists, update it
|
||||
for (auto& it : *s_osFunctionTable)
|
||||
{
|
||||
if (it.libHashA == libHashA &&
|
||||
it.libHashB == libHashB &&
|
||||
it.funcHashA == funcHashA &&
|
||||
it.funcHashB == funcHashB)
|
||||
{
|
||||
it.hleFunc = PPCInterpreter_registerHLECall(osFunction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
s_osFunctionTable->emplace_back(libHashA, libHashB, funcHashA, funcHashB, fmt::format("{}.{}", libraryName, functionName), PPCInterpreter_registerHLECall(osFunction));
|
||||
}
|
||||
|
||||
__declspec(dllexport) void osLib_registerHLEFunction(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t* hCPU))
|
||||
{
|
||||
osLib_addFunctionInternal(libraryName, functionName, osFunction);
|
||||
}
|
||||
|
||||
sint32 osLib_getFunctionIndex(const char* libraryName, const char* functionName)
|
||||
{
|
||||
uint32 libHashA, libHashB;
|
||||
uint32 funcHashA, funcHashB;
|
||||
osLib_generateHashFromName(libraryName, &libHashA, &libHashB);
|
||||
osLib_generateHashFromName(functionName, &funcHashA, &funcHashB);
|
||||
for (auto& it : *s_osFunctionTable)
|
||||
{
|
||||
if (it.libHashA == libHashA &&
|
||||
it.libHashB == libHashB &&
|
||||
it.funcHashA == funcHashA &&
|
||||
it.funcHashB == funcHashB)
|
||||
{
|
||||
return it.hleFunc;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void osLib_addVirtualPointer(const char* libraryName, const char* functionName, uint32 vPtr)
|
||||
{
|
||||
// calculate hash
|
||||
uint32 libHashA, libHashB;
|
||||
uint32 funcHashA, funcHashB;
|
||||
osLib_generateHashFromName(libraryName, &libHashA, &libHashB);
|
||||
osLib_generateHashFromName(functionName, &funcHashA, &funcHashB);
|
||||
// if entry already exists, update it
|
||||
for (auto& it : osDataTable)
|
||||
{
|
||||
if (it.libHashA == libHashA &&
|
||||
it.libHashB == libHashB &&
|
||||
it.funcHashA == funcHashA &&
|
||||
it.funcHashB == funcHashB)
|
||||
{
|
||||
it.vPtr = vPtr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// add entry
|
||||
auto writeIndex = osDataTable.size();
|
||||
osDataTable.resize(osDataTable.size() + 1);
|
||||
osDataTable[writeIndex].libHashA = libHashA;
|
||||
osDataTable[writeIndex].libHashB = libHashB;
|
||||
osDataTable[writeIndex].funcHashA = funcHashA;
|
||||
osDataTable[writeIndex].funcHashB = funcHashB;
|
||||
osDataTable[writeIndex].vPtr = vPtr;
|
||||
}
|
||||
|
||||
uint32 osLib_getPointer(const char* libraryName, const char* functionName)
|
||||
{
|
||||
uint32 libHashA, libHashB;
|
||||
uint32 funcHashA, funcHashB;
|
||||
osLib_generateHashFromName(libraryName, &libHashA, &libHashB);
|
||||
osLib_generateHashFromName(functionName, &funcHashA, &funcHashB);
|
||||
for (auto& it : osDataTable)
|
||||
{
|
||||
if (it.libHashA == libHashA &&
|
||||
it.libHashB == libHashB &&
|
||||
it.funcHashA == funcHashA &&
|
||||
it.funcHashB == funcHashB)
|
||||
{
|
||||
return it.vPtr;
|
||||
}
|
||||
}
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
void osLib_returnFromFunction(PPCInterpreter_t* hCPU, uint32 returnValue)
|
||||
{
|
||||
hCPU->gpr[3] = returnValue;
|
||||
hCPU->instructionPointer = hCPU->spr.LR;
|
||||
}
|
||||
|
||||
void osLib_returnFromFunction64(PPCInterpreter_t* hCPU, uint64 returnValue64)
|
||||
{
|
||||
hCPU->gpr[3] = (returnValue64>>32)&0xFFFFFFFF;
|
||||
hCPU->gpr[4] = (returnValue64>>0)&0xFFFFFFFF;
|
||||
hCPU->instructionPointer = hCPU->spr.LR;
|
||||
}
|
||||
|
||||
void osLib_load()
|
||||
{
|
||||
// load HLE modules
|
||||
coreinit_load();
|
||||
zlib::load();
|
||||
gx2_load();
|
||||
dmae_load();
|
||||
padscore::load();
|
||||
vpad::load();
|
||||
snd_core::loadExports();
|
||||
nn::erreula::load();
|
||||
nnAct_load();
|
||||
nn::acp::load();
|
||||
nnAc_load();
|
||||
nnEc_load();
|
||||
nnBoss_load();
|
||||
nn::nfp::load();
|
||||
nnUds_load();
|
||||
nn::nim::load();
|
||||
nn::ndm::load();
|
||||
nn::save::load();
|
||||
nsysnet_load();
|
||||
nn::fp::load();
|
||||
nn::olv::load();
|
||||
nn::idbe::load();
|
||||
nlibnss::load();
|
||||
nlibcurl::load();
|
||||
sysapp_load();
|
||||
nsyshid::load();
|
||||
nsyskbd::nsyskbd_load();
|
||||
swkbd::load();
|
||||
camera::load();
|
||||
procui_load();
|
||||
}
|
||||
25
src/Cafe/OS/common/OSCommon.h
Normal file
25
src/Cafe/OS/common/OSCommon.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
struct PPCInterpreter_t;
|
||||
|
||||
|
||||
#define OSLIB_FUNCTIONTABLE_TYPE_FUNCTION (1)
|
||||
#define OSLIB_FUNCTIONTABLE_TYPE_POINTER (2)
|
||||
|
||||
void osLib_load();
|
||||
void osLib_generateHashFromName(const char* name, uint32* hashA, uint32* hashB);
|
||||
sint32 osLib_getFunctionIndex(const char* libraryName, const char* functionName);
|
||||
uint32 osLib_getPointer(const char* libraryName, const char* functionName);
|
||||
|
||||
void osLib_addFunctionInternal(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t* hCPU));
|
||||
#define osLib_addFunction(__p1, __p2, __p3) osLib_addFunctionInternal((const char*)__p1, __p2, __p3)
|
||||
void osLib_addVirtualPointer(const char* libraryName, const char* functionName, uint32 vPtr);
|
||||
|
||||
void osLib_returnFromFunction(PPCInterpreter_t* hCPU, uint32 returnValue);
|
||||
void osLib_returnFromFunction64(PPCInterpreter_t* hCPU, uint64 returnValue64);
|
||||
|
||||
// libs
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
|
||||
// utility functions
|
||||
#include "Cafe/OS/common/OSUtil.h"
|
||||
233
src/Cafe/OS/common/OSUtil.h
Normal file
233
src/Cafe/OS/common/OSUtil.h
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
#pragma once
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
#include "Cafe/HW/Espresso/PPCState.h"
|
||||
#include "Cafe/HW/MMU/MMU.h"
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include <fmt/compile.h>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
class cafeExportParamWrapper
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
static void getParamWrapper(PPCInterpreter_t* hCPU, int& gprIndex, int& fprIndex, T& v)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<T>)
|
||||
{
|
||||
uint32be addr;
|
||||
if (gprIndex >= 8)
|
||||
addr = memory_readU32(hCPU->gpr[1] + 8 + (gprIndex - 8) * 4);
|
||||
else
|
||||
addr = hCPU->gpr[3 + gprIndex];
|
||||
|
||||
using TPtr = std::remove_pointer_t<T>;
|
||||
v = MEMPTR<TPtr>(addr).GetPtr();
|
||||
gprIndex++;
|
||||
}
|
||||
else if constexpr (std::is_base_of_v<MEMPTRBase, T>)
|
||||
{
|
||||
uint32be addr;
|
||||
if (gprIndex >= 8)
|
||||
addr = memory_readU32(hCPU->gpr[1] + 8 + (gprIndex - 8) * 4);
|
||||
else
|
||||
addr = hCPU->gpr[3 + gprIndex];
|
||||
|
||||
v = addr.value();
|
||||
gprIndex++;
|
||||
}
|
||||
else if constexpr (std::is_enum_v<T>)
|
||||
{
|
||||
using TEnum = std::underlying_type_t<T>;
|
||||
getParamWrapper<TEnum>(hCPU, gprIndex, fprIndex, (TEnum&)v);
|
||||
}
|
||||
else if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
if constexpr (sizeof(T) == sizeof(uint64))
|
||||
{
|
||||
gprIndex = (gprIndex + 1)&~1;
|
||||
if (gprIndex >= 8)
|
||||
v = (T)memory_readU64(hCPU->gpr[1] + 8 + (gprIndex - 8) * 4);
|
||||
else
|
||||
v = (T)(((uint64)hCPU->gpr[3 + gprIndex]) << 32) | ((uint64)hCPU->gpr[3 + gprIndex + 1]);
|
||||
|
||||
gprIndex += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gprIndex >= 8)
|
||||
v = (T)memory_readU32(hCPU->gpr[1] + 8 + (gprIndex - 8) * 4);
|
||||
else
|
||||
v = (T)hCPU->gpr[3 + gprIndex];
|
||||
|
||||
gprIndex++;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_floating_point_v<T>)
|
||||
{
|
||||
v = (T)ppcInterpreterCurrentInstance->fpr[1 + fprIndex].fpr;
|
||||
fprIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_dbg();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void setReturnResult(PPCInterpreter_t* hCPU, T r)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<T>)
|
||||
{
|
||||
hCPU->gpr[3] = MEMPTR(r).GetMPTR();
|
||||
}
|
||||
else if constexpr (std::is_reference_v<T>)
|
||||
{
|
||||
hCPU->gpr[3] = MEMPTR(&r).GetMPTR();
|
||||
}
|
||||
else if constexpr (std::is_enum_v<T>)
|
||||
{
|
||||
using TEnum = std::underlying_type_t<T>;
|
||||
setReturnResult<TEnum>(hCPU, (TEnum)r);
|
||||
}
|
||||
else if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
if constexpr(sizeof(T) == 8)
|
||||
{
|
||||
const auto t = static_cast<uint64>(r);
|
||||
hCPU->gpr[3] = (uint32)(t >> 32); // high
|
||||
hCPU->gpr[4] = (uint32)(t); // low
|
||||
}
|
||||
else
|
||||
{
|
||||
hCPU->gpr[3] = (uint32)r;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
//static_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static auto getFormatResult(T r)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<T>)
|
||||
return MEMPTR(r).GetMPTR();
|
||||
else if constexpr (std::is_enum_v<T>)
|
||||
return static_cast<std::underlying_type_t<T>>(r);
|
||||
else if constexpr(!std::is_fundamental_v<T>)
|
||||
return MEMPTR(&r).GetMPTR();
|
||||
else
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T cafeExportGetParamWrapper(PPCInterpreter_t* hCPU, int& gprIndex, int& fprIndex)
|
||||
{
|
||||
T v;
|
||||
cafeExportParamWrapper::getParamWrapper(hCPU, gprIndex, fprIndex, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename R, typename ... Args>
|
||||
static std::tuple<Args...> cafeExportBuildArgTuple(PPCInterpreter_t* hCPU, R(fn)(Args...))
|
||||
{
|
||||
int gprIndex = 0;
|
||||
int fprIndex = 0;
|
||||
return std::tuple<Args...>{ cafeExportGetParamWrapper<Args>(hCPU, gprIndex, fprIndex)... };
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
using _CAFE_FORMAT_ARG = std::conditional_t<std::is_pointer_v<T>,
|
||||
std::conditional_t<std::is_same_v<T, char*> || std::is_same_v<T, const char*>, T, MEMPTR<T>>, T>;
|
||||
|
||||
template <typename R, typename... Args>
|
||||
static auto cafeExportBuildFormatTuple(PPCInterpreter_t* hCPU, R(fn)(Args...))
|
||||
{
|
||||
int gprIndex = 0;
|
||||
int fprIndex = 0;
|
||||
return std::tuple<_CAFE_FORMAT_ARG<Args>...>{
|
||||
cafeExportGetParamWrapper<_CAFE_FORMAT_ARG<Args>>(hCPU, gprIndex, fprIndex)...
|
||||
};
|
||||
}
|
||||
|
||||
template<auto fn, typename TNames, LogType TLogType>
|
||||
void cafeExportCallWrapper(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
auto tup = cafeExportBuildArgTuple(hCPU, fn);
|
||||
bool shouldLog = false;
|
||||
if (cemuLog_isLoggingEnabled(TLogType))
|
||||
{
|
||||
const auto format_tup = cafeExportBuildFormatTuple(hCPU, fn);
|
||||
if(cemuLog_advancedPPCLoggingEnabled())
|
||||
{
|
||||
MPTR threadMPTR = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread());
|
||||
if constexpr (std::tuple_size<decltype(format_tup)>::value > 0)
|
||||
shouldLog = cemuLog_log(TLogType, "{}.{}{} # LR: {:#x} | Thread: {:#x}", TNames::GetLib(), TNames::GetFunc(), format_tup, hCPU->spr.LR, threadMPTR);
|
||||
else
|
||||
shouldLog = cemuLog_log(TLogType, "{}.{}() # LR: {:#x} | Thread: {:#x}", TNames::GetLib(), TNames::GetFunc(), hCPU->spr.LR, threadMPTR);
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (std::tuple_size<decltype(format_tup)>::value > 0)
|
||||
{
|
||||
shouldLog = cemuLog_log(TLogType, "{}.{}{}", TNames::GetLib(), TNames::GetFunc(), format_tup);
|
||||
}
|
||||
else
|
||||
shouldLog = cemuLog_log(TLogType, "{}.{}()", TNames::GetLib(), TNames::GetFunc());
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (!std::is_void<decltype(std::apply(fn, tup))>::value)
|
||||
{
|
||||
// has non-void return type
|
||||
decltype(auto) result = std::apply(fn, tup);
|
||||
cafeExportParamWrapper::setReturnResult<decltype(std::apply(fn, tup))>(hCPU, result);
|
||||
if(shouldLog)
|
||||
cemuLog_log(TLogType, "\t\t{}.{} -> {}", TNames::GetLib(), TNames::GetFunc(), cafeExportParamWrapper::getFormatResult(result));
|
||||
}
|
||||
else
|
||||
{
|
||||
// return type is void
|
||||
std::apply(fn, tup);
|
||||
}
|
||||
// return from func
|
||||
hCPU->instructionPointer = hCPU->spr.LR;
|
||||
}
|
||||
|
||||
void osLib_addFunctionInternal(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t* hCPU));
|
||||
|
||||
template<auto fn, typename TNames, LogType TLogType>
|
||||
void cafeExportMakeWrapper(const char* libname, const char* funcname)
|
||||
{
|
||||
osLib_addFunctionInternal(libname, funcname, &cafeExportCallWrapper<fn, TNames, TLogType>);
|
||||
}
|
||||
|
||||
#define cafeExportRegister(__libname, __func, __logtype) \
|
||||
{ \
|
||||
struct StringWrapper { \
|
||||
static const char* GetLib() { return __libname; }; \
|
||||
static const char* GetFunc() { return #__func; }; \
|
||||
}; \
|
||||
cafeExportMakeWrapper<__func, StringWrapper, __logtype>(__libname, # __func);\
|
||||
}
|
||||
|
||||
#define cafeExportRegisterFunc(__func, __libname, __funcname, __logtype) \
|
||||
{\
|
||||
struct StringWrapper { \
|
||||
static const char* GetLib() { return __libname; }; \
|
||||
static const char* GetFunc() { return __funcname; }; \
|
||||
}; \
|
||||
cafeExportMakeWrapper<__func, StringWrapper, __logtype>(__libname, __funcname);\
|
||||
}
|
||||
|
||||
template<auto fn>
|
||||
MPTR makeCallableExport()
|
||||
{
|
||||
return PPCInterpreter_makeCallableExportDepr(&cafeExportCallWrapper<fn, "CALLABLE_EXPORT">);
|
||||
}
|
||||
|
||||
void osLib_addVirtualPointer(const char* libraryName, const char* functionName, uint32 vPtr);
|
||||
68
src/Cafe/OS/common/PPCConcurrentQueue.h
Normal file
68
src/Cafe/OS/common/PPCConcurrentQueue.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||
|
||||
template <typename T>
|
||||
class PPCConcurrentQueue
|
||||
{
|
||||
public:
|
||||
PPCConcurrentQueue() {}
|
||||
PPCConcurrentQueue(const PPCConcurrentQueue&) = delete;
|
||||
PPCConcurrentQueue& operator=(const PPCConcurrentQueue&) = delete;
|
||||
|
||||
void push(const T& item, OSThread_t* thread)
|
||||
{
|
||||
//if(thread == nullptr)
|
||||
// thread = coreinitThread_getCurrentThread(ppcInterpreterCurrentInstance);
|
||||
//OSThread_t* currentThread = coreinit::OSGetCurrentThread();
|
||||
//cemu_assert_debug(thread == nullptr || currentThread == thread);
|
||||
|
||||
// forceLogDebug_printf("push suspend count: %d", _swapEndianU32(thread->suspend) - m_suspendCount);
|
||||
|
||||
//__OSLockScheduler();
|
||||
|
||||
__OSLockScheduler();
|
||||
m_queue.push(item);
|
||||
coreinit::__OSResumeThreadInternal(thread, 1);
|
||||
__OSUnlockScheduler();
|
||||
|
||||
//__OSUnlockScheduler();
|
||||
|
||||
//m_prevSuspendCount = _swapEndianU32(thread->suspend) - m_suspendCount;
|
||||
//coreinit_resumeThread(thread, _swapEndianU32(thread->suspend));
|
||||
}
|
||||
|
||||
T pop(OSThread_t* thread = nullptr)
|
||||
{
|
||||
//if (thread == nullptr)
|
||||
// thread = coreinitThread_getCurrentThread(ppcInterpreterCurrentInstance);
|
||||
|
||||
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
|
||||
cemu_assert_debug(thread == nullptr || currentThread == thread);
|
||||
|
||||
//thread = coreinitThread_getCurrentThread(ppcInterpreterCurrentInstance);
|
||||
|
||||
// forceLogDebug_printf("pop suspend count: %d", _swapEndianU32(thread->suspend) + m_suspendCount);
|
||||
|
||||
__OSLockScheduler();
|
||||
if (m_queue.empty())
|
||||
coreinit::__OSSuspendThreadInternal(thread);
|
||||
auto val = m_queue.front();
|
||||
m_queue.pop();
|
||||
__OSUnlockScheduler();
|
||||
|
||||
//coreinit_suspendThread(thread, m_suspendCount + m_prevSuspendCount);
|
||||
//m_prevSuspendCount = 0;
|
||||
//PPCCore_switchToScheduler();
|
||||
|
||||
return val;
|
||||
}
|
||||
private:
|
||||
//const int m_suspendCount = 8000;
|
||||
std::queue<T> m_queue;
|
||||
//std::atomic<uint32> m_prevSuspendCount;
|
||||
};
|
||||
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();
|
||||
}
|
||||
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