Merge branch 'threading' of https://github.com/bunnei/citra
Conflicts: src/core/hle/function_wrappers.h src/core/hle/service/gsp.cpp
This commit is contained in:
commit
004df76795
|
@ -48,7 +48,7 @@ void DisassemblerWidget::Init()
|
||||||
unsigned int curInstAddr = base_addr;
|
unsigned int curInstAddr = base_addr;
|
||||||
char result[255];
|
char result[255];
|
||||||
|
|
||||||
for (int i = 0; i < 10000; i++) // fixed for now
|
for (int i = 0; i < 20000; i++) // fixed for now
|
||||||
{
|
{
|
||||||
disasm->disasm(curInstAddr, Memory::Read32(curInstAddr), result);
|
disasm->disasm(curInstAddr, Memory::Read32(curInstAddr), result);
|
||||||
model->setItem(i, 0, new QStandardItem(QString("0x%1").arg((uint)(curInstAddr), 8, 16, QLatin1Char('0'))));
|
model->setItem(i, 0, new QStandardItem(QString("0x%1").arg((uint)(curInstAddr), 8, 16, QLatin1Char('0'))));
|
||||||
|
|
|
@ -259,14 +259,17 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
|
||||||
|
|
||||||
switch (Level)
|
switch (Level)
|
||||||
{
|
{
|
||||||
|
case OS_LEVEL: // light yellow
|
||||||
|
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||||
|
break;
|
||||||
case NOTICE_LEVEL: // light green
|
case NOTICE_LEVEL: // light green
|
||||||
Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||||
break;
|
break;
|
||||||
case ERROR_LEVEL: // light red
|
case ERROR_LEVEL: // light red
|
||||||
Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
|
Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
|
||||||
break;
|
break;
|
||||||
case WARNING_LEVEL: // light yellow
|
case WARNING_LEVEL: // light purple
|
||||||
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||||
break;
|
break;
|
||||||
case INFO_LEVEL: // cyan
|
case INFO_LEVEL: // cyan
|
||||||
Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||||
|
@ -278,15 +281,8 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
|
||||||
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (strlen(Text) > 10)
|
|
||||||
{
|
|
||||||
// First 10 chars white
|
|
||||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
|
||||||
WriteConsole(hConsole, Text, 10, &cCharsWritten, NULL);
|
|
||||||
Text += 10;
|
|
||||||
}
|
|
||||||
SetConsoleTextAttribute(hConsole, Color);
|
SetConsoleTextAttribute(hConsole, Color);
|
||||||
WriteConsole(hConsole, Text, (DWORD)strlen(Text), &cCharsWritten, NULL);
|
printf(Text);
|
||||||
#else
|
#else
|
||||||
char ColorAttr[16] = "";
|
char ColorAttr[16] = "";
|
||||||
char ResetAttr[16] = "";
|
char ResetAttr[16] = "";
|
||||||
|
|
|
@ -7,11 +7,14 @@
|
||||||
|
|
||||||
#define LOGGING
|
#define LOGGING
|
||||||
|
|
||||||
#define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports.
|
enum {
|
||||||
#define ERROR_LEVEL 2 // Critical errors
|
OS_LEVEL, // Printed by the emulated operating system
|
||||||
#define WARNING_LEVEL 3 // Something is suspicious.
|
NOTICE_LEVEL, // VERY important information that is NOT errors. Like startup and OSReports.
|
||||||
#define INFO_LEVEL 4 // General information.
|
ERROR_LEVEL, // Critical errors
|
||||||
#define DEBUG_LEVEL 5 // Detailed debugging - might make things slow.
|
WARNING_LEVEL, // Something is suspicious.
|
||||||
|
INFO_LEVEL, // General information.
|
||||||
|
DEBUG_LEVEL, // Detailed debugging - might make things slow.
|
||||||
|
};
|
||||||
|
|
||||||
namespace LogTypes
|
namespace LogTypes
|
||||||
{
|
{
|
||||||
|
@ -70,6 +73,7 @@ enum LOG_TYPE {
|
||||||
|
|
||||||
// FIXME: should this be removed?
|
// FIXME: should this be removed?
|
||||||
enum LOG_LEVELS {
|
enum LOG_LEVELS {
|
||||||
|
LOS = OS_LEVEL,
|
||||||
LNOTICE = NOTICE_LEVEL,
|
LNOTICE = NOTICE_LEVEL,
|
||||||
LERROR = ERROR_LEVEL,
|
LERROR = ERROR_LEVEL,
|
||||||
LWARNING = WARNING_LEVEL,
|
LWARNING = WARNING_LEVEL,
|
||||||
|
@ -82,31 +86,34 @@ enum LOG_LEVELS {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type,
|
void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line,
|
||||||
const char *file, int line, const char *fmt, ...)
|
const char* function, const char* fmt, ...)
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
__attribute__((format(printf, 5, 6)))
|
__attribute__((format(printf, 6, 7)))
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
|
|
||||||
#if defined LOGGING || defined _DEBUG || defined DEBUGFAST
|
#if defined LOGGING || defined _DEBUG || defined DEBUGFAST
|
||||||
#define MAX_LOGLEVEL DEBUG_LEVEL
|
#define MAX_LOGLEVEL LDEBUG
|
||||||
#else
|
#else
|
||||||
#ifndef MAX_LOGLEVEL
|
#ifndef MAX_LOGLEVEL
|
||||||
#define MAX_LOGLEVEL WARNING_LEVEL
|
#define MAX_LOGLEVEL LWARNING
|
||||||
#endif // loglevel
|
#endif // loglevel
|
||||||
#endif // logging
|
#endif // logging
|
||||||
|
|
||||||
#ifdef GEKKO
|
#ifdef _WIN32
|
||||||
#define GENERIC_LOG(t, v, ...)
|
#ifndef __func__
|
||||||
#else
|
#define __func__ __FUNCTION__
|
||||||
// Let the compiler optimize this out
|
#endif
|
||||||
#define GENERIC_LOG(t, v, ...) { \
|
|
||||||
if (v <= MAX_LOGLEVEL) \
|
|
||||||
GenericLog(v, t, __FILE__, __LINE__, __VA_ARGS__); \
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Let the compiler optimize this out
|
||||||
|
#define GENERIC_LOG(t, v, ...) { \
|
||||||
|
if (v <= LogTypes::MAX_LOGLEVEL) \
|
||||||
|
GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0)
|
||||||
#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0)
|
#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0)
|
||||||
#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0)
|
#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0)
|
||||||
#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0)
|
#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0)
|
||||||
|
|
|
@ -10,14 +10,16 @@
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
|
||||||
void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
|
void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
|
||||||
const char *file, int line, const char* fmt, ...)
|
const char* function, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
if (LogManager::GetInstance())
|
|
||||||
|
if (LogManager::GetInstance()) {
|
||||||
LogManager::GetInstance()->Log(level, type,
|
LogManager::GetInstance()->Log(level, type,
|
||||||
file, line, fmt, args);
|
file, line, function, fmt, args);
|
||||||
|
}
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +90,8 @@ LogManager::LogManager()
|
||||||
m_Log[i]->AddListener(m_debuggerLog);
|
m_Log[i]->AddListener(m_debuggerLog);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_consoleLog->Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
LogManager::~LogManager()
|
LogManager::~LogManager()
|
||||||
|
@ -107,8 +111,8 @@ LogManager::~LogManager()
|
||||||
delete m_debuggerLog;
|
delete m_debuggerLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
|
void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
|
||||||
const char *file, int line, const char *format, va_list args)
|
int line, const char* function, const char *fmt, va_list args)
|
||||||
{
|
{
|
||||||
char temp[MAX_MSGLEN];
|
char temp[MAX_MSGLEN];
|
||||||
char msg[MAX_MSGLEN * 2];
|
char msg[MAX_MSGLEN * 2];
|
||||||
|
@ -117,17 +121,15 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
|
||||||
if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners())
|
if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CharArrayFromFormatV(temp, MAX_MSGLEN, format, args);
|
CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args);
|
||||||
|
|
||||||
|
static const char level_to_char[7] = "ONEWID";
|
||||||
|
sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line,
|
||||||
|
level_to_char[(int)level], log->GetShortName(), function, temp);
|
||||||
|
|
||||||
static const char level_to_char[7] = "-NEWID";
|
|
||||||
sprintf(msg, "%s %s:%u %c[%s]: %s\n",
|
|
||||||
Common::Timer::GetTimeFormatted().c_str(),
|
|
||||||
file, line, level_to_char[(int)level],
|
|
||||||
log->GetShortName(), temp);
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
Host_SysMessage(msg);
|
Host_SysMessage(msg);
|
||||||
#endif
|
#endif
|
||||||
printf(msg); // TODO(ShizZy): RemoveMe when I no longer need this
|
|
||||||
log->Trigger(level, msg);
|
log->Trigger(level, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +149,7 @@ LogContainer::LogContainer(const char* shortName, const char* fullName, bool ena
|
||||||
{
|
{
|
||||||
strncpy(m_fullName, fullName, 128);
|
strncpy(m_fullName, fullName, 128);
|
||||||
strncpy(m_shortName, shortName, 32);
|
strncpy(m_shortName, shortName, 32);
|
||||||
m_level = (LogTypes::LOG_LEVELS)MAX_LOGLEVEL;
|
m_level = LogTypes::MAX_LOGLEVEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogContainer
|
// LogContainer
|
||||||
|
|
|
@ -97,10 +97,10 @@ private:
|
||||||
~LogManager();
|
~LogManager();
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static u32 GetMaxLevel() { return MAX_LOGLEVEL; }
|
static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL; }
|
||||||
|
|
||||||
void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
|
void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
|
||||||
const char *file, int line, const char *fmt, va_list args);
|
const char* function, const char *fmt, va_list args);
|
||||||
|
|
||||||
void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
|
void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,12 +34,14 @@ set(SRCS core.cpp
|
||||||
hle/config_mem.cpp
|
hle/config_mem.cpp
|
||||||
hle/coprocessor.cpp
|
hle/coprocessor.cpp
|
||||||
hle/svc.cpp
|
hle/svc.cpp
|
||||||
|
hle/kernel/event.cpp
|
||||||
hle/kernel/kernel.cpp
|
hle/kernel/kernel.cpp
|
||||||
hle/kernel/mutex.cpp
|
hle/kernel/mutex.cpp
|
||||||
hle/kernel/thread.cpp
|
hle/kernel/thread.cpp
|
||||||
hle/service/apt.cpp
|
hle/service/apt.cpp
|
||||||
hle/service/gsp.cpp
|
hle/service/gsp.cpp
|
||||||
hle/service/hid.cpp
|
hle/service/hid.cpp
|
||||||
|
hle/service/ndm.cpp
|
||||||
hle/service/service.cpp
|
hle/service/service.cpp
|
||||||
hle/service/srv.cpp
|
hle/service/srv.cpp
|
||||||
hw/gpu.cpp
|
hw/gpu.cpp
|
||||||
|
|
|
@ -89,6 +89,9 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void LoadContext(const ThreadContext& ctx) = 0;
|
virtual void LoadContext(const ThreadContext& ctx) = 0;
|
||||||
|
|
||||||
|
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||||
|
virtual void PrepareReschedule() = 0;
|
||||||
|
|
||||||
/// Getter for num_instructions
|
/// Getter for num_instructions
|
||||||
u64 GetNumInstructions() {
|
u64 GetNumInstructions() {
|
||||||
return num_instructions;
|
return num_instructions;
|
||||||
|
|
|
@ -98,7 +98,7 @@ u64 ARM_Interpreter::GetTicks() const {
|
||||||
* @param num_instructions Number of instructions to executes
|
* @param num_instructions Number of instructions to executes
|
||||||
*/
|
*/
|
||||||
void ARM_Interpreter::ExecuteInstructions(int num_instructions) {
|
void ARM_Interpreter::ExecuteInstructions(int num_instructions) {
|
||||||
state->NumInstrsToExecute = num_instructions;
|
state->NumInstrsToExecute = num_instructions - 1;
|
||||||
ARMul_Emulate32(state);
|
ARMul_Emulate32(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +118,9 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
|
||||||
|
|
||||||
ctx.fpscr = state->VFP[1];
|
ctx.fpscr = state->VFP[1];
|
||||||
ctx.fpexc = state->VFP[2];
|
ctx.fpexc = state->VFP[2];
|
||||||
|
|
||||||
|
ctx.reg_15 = state->Reg[15];
|
||||||
|
ctx.mode = state->NextInstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,6 +140,11 @@ void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
|
||||||
state->VFP[1] = ctx.fpscr;
|
state->VFP[1] = ctx.fpscr;
|
||||||
state->VFP[2] = ctx.fpexc;
|
state->VFP[2] = ctx.fpexc;
|
||||||
|
|
||||||
state->Reg[15] = ctx.pc;
|
state->Reg[15] = ctx.reg_15;
|
||||||
state->NextInstr = RESUME;
|
state->NextInstr = ctx.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||||
|
void ARM_Interpreter::PrepareReschedule() {
|
||||||
|
state->NumInstrsToExecute = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,9 @@ public:
|
||||||
*/
|
*/
|
||||||
void LoadContext(const ThreadContext& ctx);
|
void LoadContext(const ThreadContext& ctx);
|
||||||
|
|
||||||
|
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||||
|
void PrepareReschedule();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4456,6 +4456,7 @@ ARMul_Emulate26 (ARMul_State * state)
|
||||||
}
|
}
|
||||||
/* Drop through. */
|
/* Drop through. */
|
||||||
|
|
||||||
|
case 0xe0:
|
||||||
case 0xe4:
|
case 0xe4:
|
||||||
case 0xe6:
|
case 0xe6:
|
||||||
case 0xe8:
|
case 0xe8:
|
||||||
|
@ -4489,7 +4490,6 @@ ARMul_Emulate26 (ARMul_State * state)
|
||||||
|
|
||||||
|
|
||||||
/* Co-Processor Register Transfers (MRC) and Data Ops. */
|
/* Co-Processor Register Transfers (MRC) and Data Ops. */
|
||||||
case 0xe0:
|
|
||||||
case 0xe1:
|
case 0xe1:
|
||||||
case 0xe3:
|
case 0xe3:
|
||||||
case 0xe5:
|
case 0xe5:
|
||||||
|
@ -4533,23 +4533,7 @@ ARMul_Emulate26 (ARMul_State * state)
|
||||||
case 0xfd:
|
case 0xfd:
|
||||||
case 0xfe:
|
case 0xfe:
|
||||||
case 0xff:
|
case 0xff:
|
||||||
if (instr == ARMul_ABORTWORD
|
|
||||||
&& state->AbortAddr == pc) {
|
|
||||||
/* A prefetch abort. */
|
|
||||||
XScale_set_fsr_far (state,
|
|
||||||
ARMul_CP15_R5_MMU_EXCPT,
|
|
||||||
pc);
|
|
||||||
ARMul_Abort (state,
|
|
||||||
ARMul_PrefetchAbortV);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//sky_pref_t* pref = get_skyeye_pref();
|
|
||||||
//if(pref->user_mode_sim){
|
|
||||||
// ARMul_OSHandleSWI (state, BITS (0, 23));
|
|
||||||
// break;
|
|
||||||
//}
|
|
||||||
HLE::CallSVC(instr);
|
HLE::CallSVC(instr);
|
||||||
ARMul_Abort (state, ARMul_SWIV);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,24 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/mem_map.h"
|
#include "core/mem_map.h"
|
||||||
#include "core/hw/hw.h"
|
#include "core/hw/hw.h"
|
||||||
|
#include "core/hw/gpu.h"
|
||||||
#include "core/arm/disassembler/arm_disasm.h"
|
#include "core/arm/disassembler/arm_disasm.h"
|
||||||
#include "core/arm/interpreter/arm_interpreter.h"
|
#include "core/arm/interpreter/arm_interpreter.h"
|
||||||
|
|
||||||
|
#include "core/hle/hle.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
ARM_Disasm* g_disasm = NULL; ///< ARM disassembler
|
u64 g_last_ticks = 0; ///< Last CPU ticks
|
||||||
ARM_Interface* g_app_core = NULL; ///< ARM11 application core
|
ARM_Disasm* g_disasm = nullptr; ///< ARM disassembler
|
||||||
ARM_Interface* g_sys_core = NULL; ///< ARM11 system (OS) core
|
ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
|
||||||
|
ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
|
||||||
|
|
||||||
/// Run the core CPU loop
|
/// Run the core CPU loop
|
||||||
void RunLoop() {
|
void RunLoop() {
|
||||||
for (;;){
|
for (;;){
|
||||||
g_app_core->Run(100);
|
g_app_core->Run(GPU::kFrameTicks);
|
||||||
HW::Update();
|
HW::Update();
|
||||||
Kernel::Reschedule();
|
Kernel::Reschedule();
|
||||||
}
|
}
|
||||||
|
@ -32,8 +35,14 @@ void RunLoop() {
|
||||||
/// Step the CPU one instruction
|
/// Step the CPU one instruction
|
||||||
void SingleStep() {
|
void SingleStep() {
|
||||||
g_app_core->Step();
|
g_app_core->Step();
|
||||||
HW::Update();
|
|
||||||
Kernel::Reschedule();
|
// Update and reschedule after approx. 1 frame
|
||||||
|
u64 current_ticks = Core::g_app_core->GetTicks();
|
||||||
|
if ((current_ticks - g_last_ticks) >= GPU::kFrameTicks || HLE::g_reschedule) {
|
||||||
|
g_last_ticks = current_ticks;
|
||||||
|
HW::Update();
|
||||||
|
Kernel::Reschedule();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Halt the core
|
/// Halt the core
|
||||||
|
@ -54,6 +63,8 @@ int Init() {
|
||||||
g_app_core = new ARM_Interpreter();
|
g_app_core = new ARM_Interpreter();
|
||||||
g_sys_core = new ARM_Interpreter();
|
g_sys_core = new ARM_Interpreter();
|
||||||
|
|
||||||
|
g_last_ticks = Core::g_app_core->GetTicks();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,12 +168,14 @@
|
||||||
<ClCompile Include="hle\config_mem.cpp" />
|
<ClCompile Include="hle\config_mem.cpp" />
|
||||||
<ClCompile Include="hle\coprocessor.cpp" />
|
<ClCompile Include="hle\coprocessor.cpp" />
|
||||||
<ClCompile Include="hle\hle.cpp" />
|
<ClCompile Include="hle\hle.cpp" />
|
||||||
|
<ClCompile Include="hle\kernel\event.cpp" />
|
||||||
<ClCompile Include="hle\kernel\kernel.cpp" />
|
<ClCompile Include="hle\kernel\kernel.cpp" />
|
||||||
<ClCompile Include="hle\kernel\mutex.cpp" />
|
<ClCompile Include="hle\kernel\mutex.cpp" />
|
||||||
<ClCompile Include="hle\kernel\thread.cpp" />
|
<ClCompile Include="hle\kernel\thread.cpp" />
|
||||||
<ClCompile Include="hle\service\apt.cpp" />
|
<ClCompile Include="hle\service\apt.cpp" />
|
||||||
<ClCompile Include="hle\service\gsp.cpp" />
|
<ClCompile Include="hle\service\gsp.cpp" />
|
||||||
<ClCompile Include="hle\service\hid.cpp" />
|
<ClCompile Include="hle\service\hid.cpp" />
|
||||||
|
<ClCompile Include="hle\service\ndm.cpp" />
|
||||||
<ClCompile Include="hle\service\service.cpp" />
|
<ClCompile Include="hle\service\service.cpp" />
|
||||||
<ClCompile Include="hle\service\srv.cpp" />
|
<ClCompile Include="hle\service\srv.cpp" />
|
||||||
<ClCompile Include="hle\svc.cpp" />
|
<ClCompile Include="hle\svc.cpp" />
|
||||||
|
@ -217,12 +219,14 @@
|
||||||
<ClInclude Include="hle\coprocessor.h" />
|
<ClInclude Include="hle\coprocessor.h" />
|
||||||
<ClInclude Include="hle\function_wrappers.h" />
|
<ClInclude Include="hle\function_wrappers.h" />
|
||||||
<ClInclude Include="hle\hle.h" />
|
<ClInclude Include="hle\hle.h" />
|
||||||
|
<ClInclude Include="hle\kernel\event.h" />
|
||||||
<ClInclude Include="hle\kernel\kernel.h" />
|
<ClInclude Include="hle\kernel\kernel.h" />
|
||||||
<ClInclude Include="hle\kernel\mutex.h" />
|
<ClInclude Include="hle\kernel\mutex.h" />
|
||||||
<ClInclude Include="hle\kernel\thread.h" />
|
<ClInclude Include="hle\kernel\thread.h" />
|
||||||
<ClInclude Include="hle\service\apt.h" />
|
<ClInclude Include="hle\service\apt.h" />
|
||||||
<ClInclude Include="hle\service\gsp.h" />
|
<ClInclude Include="hle\service\gsp.h" />
|
||||||
<ClInclude Include="hle\service\hid.h" />
|
<ClInclude Include="hle\service\hid.h" />
|
||||||
|
<ClInclude Include="hle\service\ndm.h" />
|
||||||
<ClInclude Include="hle\service\service.h" />
|
<ClInclude Include="hle\service\service.h" />
|
||||||
<ClInclude Include="hle\service\srv.h" />
|
<ClInclude Include="hle\service\srv.h" />
|
||||||
<ClInclude Include="hle\svc.h" />
|
<ClInclude Include="hle\svc.h" />
|
||||||
|
|
|
@ -165,6 +165,12 @@
|
||||||
<ClCompile Include="arm\interpreter\armcopro.cpp">
|
<ClCompile Include="arm\interpreter\armcopro.cpp">
|
||||||
<Filter>arm\interpreter</Filter>
|
<Filter>arm\interpreter</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="hle\kernel\event.cpp">
|
||||||
|
<Filter>hle\kernel</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="hle\service\ndm.cpp">
|
||||||
|
<Filter>hle\service</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="arm\disassembler\arm_disasm.h">
|
<ClInclude Include="arm\disassembler\arm_disasm.h">
|
||||||
|
@ -295,6 +301,12 @@
|
||||||
<ClInclude Include="hle\kernel\mutex.h">
|
<ClInclude Include="hle\kernel\mutex.h">
|
||||||
<Filter>hle\kernel</Filter>
|
<Filter>hle\kernel</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="hle\kernel\event.h">
|
||||||
|
<Filter>hle\kernel</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="hle\service\ndm.h">
|
||||||
|
<Filter>hle\service</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Text Include="CMakeLists.txt" />
|
<Text Include="CMakeLists.txt" />
|
||||||
|
|
|
@ -55,7 +55,7 @@ inline void Read(T &var, const u32 addr) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(HLE, "unknown ConfigMem::Read%d @ 0x%08X", sizeof(var) * 8, addr);
|
ERROR_LOG(HLE, "unknown addr=0x%08X", addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ s32 CallMRC(u32 instruction) {
|
||||||
return GetThreadCommandBuffer();
|
return GetThreadCommandBuffer();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
//DEBUG_LOG(OSHLE, "unknown MRC call 0x%08X", instruction);
|
DEBUG_LOG(OSHLE, "unknown MRC call 0x%08X", instruction);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
// Copyright (c) 2012- PPSSPP Project.
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// Refer to the license.txt file included.
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, version 2.0 or later versions.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License 2.0 for more details.
|
|
||||||
|
|
||||||
// A copy of the GPL 2.0 should have been included with the program.
|
|
||||||
// If not, see http://www.gnu.org/licenses/
|
|
||||||
|
|
||||||
// Official git repository and contact information can be found at
|
|
||||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
@ -21,725 +8,107 @@
|
||||||
#include "core/mem_map.h"
|
#include "core/mem_map.h"
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
|
|
||||||
// For easy parameter parsing and return value processing.
|
namespace HLE {
|
||||||
|
|
||||||
//32bit wrappers
|
#define PARAM(n) Core::g_app_core->GetReg(n)
|
||||||
template<void func()> void WrapV_V() {
|
#define RETURN(n) Core::g_app_core->SetReg(0, n)
|
||||||
func();
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Function wrappers that return type s32
|
||||||
|
|
||||||
|
template<s32 func(u32, u32, u32, u32)> void Wrap() {
|
||||||
|
RETURN(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<u32 func()> void WrapU_V() {
|
template<s32 func(u32, u32, u32, u32, u32)> void Wrap() {
|
||||||
|
RETURN(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(u32*, u32, u32, u32, u32, u32)> void Wrap(){
|
||||||
|
u32 param_1 = 0;
|
||||||
|
u32 retval = func(¶m_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
||||||
|
Core::g_app_core->SetReg(1, param_1);
|
||||||
|
RETURN(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
|
||||||
|
s32 param_1 = 0;
|
||||||
|
s32 retval = func(¶m_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
|
||||||
|
(PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)));
|
||||||
|
Core::g_app_core->SetReg(1, (u32)param_1);
|
||||||
|
RETURN(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bunnei): Is this correct? Probably not
|
||||||
|
template<s32 func(u32, u32, u32, u32, s64)> void Wrap() {
|
||||||
|
RETURN(func(PARAM(5), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(4) << 32) | PARAM(0))));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(u32, s64)> void Wrap() {
|
||||||
|
RETURN(func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(void*, void*, u32)> void Wrap(){
|
||||||
|
RETURN(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(s32*, u32)> void Wrap(){
|
||||||
|
s32 param_1 = 0;
|
||||||
|
u32 retval = func(¶m_1, PARAM(1));
|
||||||
|
Core::g_app_core->SetReg(1, param_1);
|
||||||
|
RETURN(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(u32, s32)> void Wrap() {
|
||||||
|
RETURN(func(PARAM(0), (s32)PARAM(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(u32*, u32)> void Wrap(){
|
||||||
|
u32 param_1 = 0;
|
||||||
|
u32 retval = func(¶m_1, PARAM(1));
|
||||||
|
Core::g_app_core->SetReg(1, param_1);
|
||||||
|
RETURN(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(u32)> void Wrap() {
|
||||||
|
RETURN(func(PARAM(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(void*)> void Wrap() {
|
||||||
|
RETURN(func(Memory::GetPointer(PARAM(0))));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(s64*, u32, void*, s32)> void Wrap(){
|
||||||
|
RETURN(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
|
||||||
|
(s32)PARAM(3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<s32 func(u32*, const char*)> void Wrap() {
|
||||||
|
u32 param_1 = 0;
|
||||||
|
u32 retval = func(¶m_1, Memory::GetCharPointer(PARAM(1)));
|
||||||
|
Core::g_app_core->SetReg(1, param_1);
|
||||||
|
RETURN(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Function wrappers that return type u32
|
||||||
|
|
||||||
|
template<u32 func()> void Wrap() {
|
||||||
RETURN(func());
|
RETURN(func());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int func(void *, const char *)> void WrapI_VC() {
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
u32 retval = func(Memory::GetPointer(PARAM(0)), Memory::GetCharPointer(PARAM(1)));
|
/// Function wrappers that return type void
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, void *, int)> void WrapU_IVI() {
|
|
||||||
u32 retval = func(PARAM(0), Memory::GetPointer(PARAM(1)), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, int, int, u32)> void WrapI_CIIU() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, const char *, u32, void *, void *, u32, int)> void WrapI_ICUVVUI() {
|
|
||||||
u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), Memory::GetPointer(PARAM(3)),Memory::GetPointer(PARAM(4)), PARAM(5), PARAM(6) );
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hm, do so many params get passed in registers?
|
|
||||||
template<int func(const char *, int, const char *, int, int, int, int, int)> void WrapI_CICIIIII() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), Memory::GetCharPointer(PARAM(2)),
|
|
||||||
PARAM(3), PARAM(4), PARAM(5), PARAM(6), PARAM(7));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hm, do so many params get passed in registers?
|
|
||||||
template<int func(const char *, int, int, int, int, int, int)> void WrapI_CIIIIII() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4), PARAM(5), PARAM(6));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hm, do so many params get passed in registers?
|
|
||||||
template<int func(int, int, int, int, int, int, u32)> void WrapI_IIIIIIU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hm, do so many params get passed in registers?
|
|
||||||
template<int func(int, int, int, int, int, int, int, int, u32)> void WrapI_IIIIIIIIU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6), PARAM(7), PARAM(8));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, void *)> void WrapU_IV() {
|
|
||||||
u32 retval = func(PARAM(0), Memory::GetPointer(PARAM(1)));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32)> void WrapU_U() {
|
|
||||||
u32 retval = func(PARAM(0));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int)> void WrapU_UI() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32)> void WrapI_U() {
|
|
||||||
int retval = func(PARAM(0));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, int)> void WrapI_UI() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, int, int, u32)> void WrapI_UIIU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, u32, int)> void WrapU_IUI() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32)> void WrapI_UU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, u32)> void WrapI_UUU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, u32, int)> void WrapI_UUUI() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, u32, int, int, int,int )> void WrapI_UUUIIII() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, u32, u32)> void WrapI_UUUU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, u32, u32, u32)> void WrapI_UUUUU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(void*)> void WrapI_V() {
|
|
||||||
u32 retval = func(Memory::GetPointer(PARAM(0)));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int)> void WrapU_I() {
|
|
||||||
u32 retval = func(PARAM(0));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, int, u32)> void WrapU_IIU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int)> void WrapI_I() {
|
|
||||||
int retval = func(PARAM(0));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32)> void WrapV_U() {
|
|
||||||
func(PARAM(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(int)> void WrapV_I() {
|
|
||||||
func(PARAM(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, u32)> void WrapV_UU() {
|
|
||||||
func(PARAM(0), PARAM(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(int, int)> void WrapV_II() {
|
|
||||||
func(PARAM(0), PARAM(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, const char *)> void WrapV_UC() {
|
|
||||||
func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, const char *)> void WrapI_UC() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, const char *, int)> void WrapI_UCI() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int , int , int, int, int)> void WrapU_UIIIII() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int , int , int, u32)> void WrapU_UIIIU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int , int , int, int, int, int)> void WrapU_UIIIIII() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32)> void WrapU_UU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, int)> void WrapU_UUI() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, int, int)> void WrapU_UUII() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *, u32, u32, u32)> void WrapU_CUUU() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, int, u32, int, int)> void WrapV_UIUII() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int, u32, int, int)> void WrapU_UIUII() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, int, u32, int, int)> void WrapI_UIUII() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int, u32, int)> void WrapU_UIUI() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, int, u32, int)> void WrapI_UIUI() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int, u32)> void WrapU_UIU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int, u32, u32)> void WrapU_UIUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int, int)> void WrapU_UII() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int, int, u32)> void WrapU_UIIU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, int, int, u32, u32)> void WrapI_UIIUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, int, int)> void WrapI_UUII() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, int, int, int)> void WrapI_UUIII() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, int, int, int)> void WrapV_UIII() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, int, int, int, int, int)> void WrapV_UIIIII() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, int, int)> void WrapV_UII() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, u32)> void WrapU_IU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, u32)> void WrapI_IU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, int)> void WrapI_UUI() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, u32, int, u32)> void WrapI_UUIU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, int)> void WrapI_II() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, int, int)> void WrapI_III() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, u32, int)> void WrapI_IUI() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, int, int, int)> void WrapI_IIII() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, int, int, int)> void WrapI_UIII() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, int, int, u32, int)> void WrapI_IIIUI() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, u32, u32, int, int)> void WrapI_IUUII() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, const char *, int, u32, u32)> void WrapI_ICIUU() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, int, u32)> void WrapI_IIU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(int, u32)> void WrapV_IU() {
|
|
||||||
func(PARAM(0), PARAM(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, int)> void WrapV_UI() {
|
|
||||||
func(PARAM(0), PARAM(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *)> void WrapU_C() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *, const char *, const char *, u32)> void WrapU_CCCU() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)),
|
|
||||||
Memory::GetCharPointer(PARAM(1)), Memory::GetCharPointer(PARAM(2)),
|
|
||||||
PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *)> void WrapI_C() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, u32)> void WrapI_CU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, u32, int)> void WrapI_CUI() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, const char *, int, u32)> void WrapI_ICIU() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, int, u32)> void WrapI_CIU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, u32, u32)> void WrapI_CUU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, u32, u32, u32)> void WrapI_CUUU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, const char*, int, int)> void WrapI_CCII() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, u32, u32, int, u32, u32)> void WrapI_CUUIUU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4), PARAM(5));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, int, int, u32, int, int)> void WrapI_CIIUII() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4), PARAM(5));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, int, u32, u32, u32)> void WrapI_CIUUU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, u32, u32, u32, u32, u32)> void WrapI_CUUUUU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4), PARAM(5));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *, u32)> void WrapU_CU() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
|
|
||||||
RETURN((u32) retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, const char *)> void WrapU_UC() {
|
|
||||||
u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *, u32, u32)> void WrapU_CUU() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
|
|
||||||
RETURN((u32) retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, int, int)> void WrapU_III() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, int)> void WrapU_II() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, int, int, int)> void WrapU_IIII() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, u32, u32)> void WrapU_IUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, u32, u32, u32)> void WrapU_IUUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(int, u32, u32, u32, u32)> void WrapU_IUUUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, u32)> void WrapU_UUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(int, u32, u32)> void WrapV_IUU() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(int, int, u32)> void WrapV_IIU() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, int, u32)> void WrapV_UIU() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, int, u32)> void WrapI_UIU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(int, u32, u32, u32, u32)> void WrapV_IUUUU() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, u32, u32)> void WrapV_UUU() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, u32, u32, u32)> void WrapV_UUUU() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(const char *, u32, int, u32)> void WrapV_CUIU() {
|
|
||||||
func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, u32, int, u32)> void WrapI_CUIU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, const char *, u32, int, u32)> void WrapV_UCUIU() {
|
|
||||||
func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3),
|
|
||||||
PARAM(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, const char *, u32, int, u32)> void WrapI_UCUIU() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(const char *, u32, int, int, u32)> void WrapV_CUIIU() {
|
|
||||||
func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3),
|
|
||||||
PARAM(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, u32, int, int, u32)> void WrapI_CUIIU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, u32, u32)> void WrapU_UUUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, const char *, u32, u32)> void WrapU_UCUU() {
|
|
||||||
u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, u32, int)> void WrapU_UUUI() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, u32, int, u32)> void WrapU_UUUIU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, u32, int, u32, int)> void WrapU_UUUIUI() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, int, u32)> void WrapU_UUIU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, int, int, int)> void WrapU_UIII() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, u32, u32, u32, u32)> void WrapI_IUUUU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, u32, u32, u32, u32, u32)> void WrapI_IUUUUU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, u32, int, int)> void WrapI_IUII() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
template<u32 func(u32, u32, u32, u32, u32)> void WrapU_UUUUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<void func(u32, u32, u32, u32, u32)> void WrapV_UUUUU() {
|
|
||||||
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *, const char *)> void WrapU_CC() {
|
template<void func(s64)> void Wrap() {
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)),
|
func(((s64)PARAM(1) << 32) | PARAM(0));
|
||||||
Memory::GetCharPointer(PARAM(1)));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<void func(const char*)> void WrapV_C() {
|
template<void func(const char*)> void Wrap() {
|
||||||
func(Memory::GetCharPointer(PARAM(0)));
|
func(Memory::GetCharPointer(PARAM(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<void func(const char *, int)> void WrapV_CI() {
|
#undef PARAM
|
||||||
func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
|
#undef RETURN
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *, int)> void WrapU_CI() {
|
} // namespace HLE
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *, int, int)> void WrapU_CII() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(const char *, int, u32, int, u32)> void WrapU_CIUIU() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(const char *, int, u32, int, u32, int)> void WrapU_CIUIUI() {
|
|
||||||
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
|
|
||||||
PARAM(3), PARAM(4), PARAM(5));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, u32, u32, u32, u32)> void WrapU_UUUUUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4),
|
|
||||||
PARAM(5));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, u32, u32, u32)> void WrapI_IUUU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, u32, u32)> void WrapI_IUU() {
|
|
||||||
int retval = func(PARAM(0), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<u32 func(u32, u32, u32, u32, u32, u32, u32)> void WrapU_UUUUUUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, int, u32, u32)> void WrapI_UIUU() {
|
|
||||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, const char *)> void WrapI_IC() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int func(int, const char *, const char *, u32, int)> void WrapI_ICCUI() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), Memory::GetCharPointer(PARAM(2)), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int func(int, const char *, const char *, int)> void WrapI_ICCI() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), Memory::GetCharPointer(PARAM(2)), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int func(const char *, int, int)> void WrapI_CII() {
|
|
||||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int func(int, const char *, int)> void WrapI_ICI() {
|
|
||||||
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, void *, void *, void *, void *, u32, int)> void WrapI_IVVVVUI(){
|
|
||||||
u32 retval = func(PARAM(0), Memory::GetPointer(PARAM(1)), Memory::GetPointer(PARAM(2)), Memory::GetPointer(PARAM(3)), Memory::GetPointer(PARAM(4)), PARAM(5), PARAM(6) );
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(int, const char *, u32, void *, int, int, int)> void WrapI_ICUVIII(){
|
|
||||||
u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), Memory::GetPointer(PARAM(3)), PARAM(4), PARAM(5), PARAM(6));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(void*, u32)> void WrapI_VU(){
|
|
||||||
u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(void*, void*, u32)> void WrapI_VVU(){
|
|
||||||
u32 retval = func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(void*, u32, void*, int)> void WrapI_VUVI(){
|
|
||||||
u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), PARAM(3));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(void*, u32, u32, u32, u32, u32)> void WrapI_VUUUUU(){
|
|
||||||
u32 retval = func(NULL, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(u32, s64)> void WrapI_US64() {
|
|
||||||
int retval = func(PARAM(0), PARAM64(1));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int func(void*, void*, u32, u32, s64)> void WrapI_VVUUS64() {
|
|
||||||
int retval = func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4));
|
|
||||||
RETURN(retval);
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "core/mem_map.h"
|
#include "core/mem_map.h"
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
#include "core/hle/svc.h"
|
#include "core/hle/svc.h"
|
||||||
|
#include "core/hle/kernel/thread.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -15,11 +16,13 @@ namespace HLE {
|
||||||
|
|
||||||
static std::vector<ModuleDef> g_module_db;
|
static std::vector<ModuleDef> g_module_db;
|
||||||
|
|
||||||
|
bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a new thread
|
||||||
|
|
||||||
const FunctionDef* GetSVCInfo(u32 opcode) {
|
const FunctionDef* GetSVCInfo(u32 opcode) {
|
||||||
u32 func_num = opcode & 0xFFFFFF; // 8 bits
|
u32 func_num = opcode & 0xFFFFFF; // 8 bits
|
||||||
if (func_num > 0xFF) {
|
if (func_num > 0xFF) {
|
||||||
ERROR_LOG(HLE,"Unknown SVC: 0x%02X", func_num);
|
ERROR_LOG(HLE,"unknown svc=0x%02X", func_num);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return &g_module_db[0].func_table[func_num];
|
return &g_module_db[0].func_table[func_num];
|
||||||
}
|
}
|
||||||
|
@ -33,19 +36,16 @@ void CallSVC(u32 opcode) {
|
||||||
if (info->func) {
|
if (info->func) {
|
||||||
info->func();
|
info->func();
|
||||||
} else {
|
} else {
|
||||||
ERROR_LOG(HLE, "Unimplemented SVC function %s(..)", info->name.c_str());
|
ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EatCycles(u32 cycles) {
|
void Reschedule(const char *reason) {
|
||||||
// TODO: ImplementMe
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReSchedule(const char *reason) {
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
_dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "ReSchedule: Invalid or too long reason.");
|
_dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
|
||||||
#endif
|
#endif
|
||||||
// TODO: ImplementMe
|
Core::g_app_core->PrepareReschedule();
|
||||||
|
g_reschedule = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) {
|
void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) {
|
||||||
|
|
|
@ -9,14 +9,10 @@
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#define PARAM(n) Core::g_app_core->GetReg(n)
|
|
||||||
#define PARAM64(n) (Core::g_app_core->GetReg(n) | ((u64)Core::g_app_core->GetReg(n + 1) << 32))
|
|
||||||
#define RETURN(n) Core::g_app_core->SetReg(0, n)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
namespace HLE {
|
namespace HLE {
|
||||||
|
|
||||||
|
extern bool g_reschedule; ///< If true, immediately reschedules the CPU to a new thread
|
||||||
|
|
||||||
typedef u32 Addr;
|
typedef u32 Addr;
|
||||||
typedef void (*Func)();
|
typedef void (*Func)();
|
||||||
|
|
||||||
|
@ -36,9 +32,7 @@ void RegisterModule(std::string name, int num_functions, const FunctionDef *func
|
||||||
|
|
||||||
void CallSVC(u32 opcode);
|
void CallSVC(u32 opcode);
|
||||||
|
|
||||||
void EatCycles(u32 cycles);
|
void Reschedule(const char *reason);
|
||||||
|
|
||||||
void ReSchedule(const char *reason);
|
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/common.h"
|
||||||
|
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/kernel/event.h"
|
||||||
|
#include "core/hle/kernel/thread.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class Event : public Object {
|
||||||
|
public:
|
||||||
|
const char* GetTypeName() const { return "Event"; }
|
||||||
|
const char* GetName() const { return name.c_str(); }
|
||||||
|
|
||||||
|
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; }
|
||||||
|
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; }
|
||||||
|
|
||||||
|
ResetType intitial_reset_type; ///< ResetType specified at Event initialization
|
||||||
|
ResetType reset_type; ///< Current ResetType
|
||||||
|
|
||||||
|
bool locked; ///< Event signal wait
|
||||||
|
bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
|
||||||
|
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
|
||||||
|
std::string name; ///< Name of event (optional)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for kernel object to synchronize
|
||||||
|
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result WaitSynchronization(bool* wait) {
|
||||||
|
*wait = locked;
|
||||||
|
if (locked) {
|
||||||
|
Handle thread = GetCurrentThreadHandle();
|
||||||
|
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
|
||||||
|
waiting_threads.push_back(thread);
|
||||||
|
}
|
||||||
|
Kernel::WaitCurrentThread(WAITTYPE_EVENT);
|
||||||
|
}
|
||||||
|
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
|
||||||
|
locked = true;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hackish function to set an events permanent lock state, used to pass through synch blocks
|
||||||
|
* @param handle Handle to event to change
|
||||||
|
* @param permanent_locked Boolean permanent locked value to set event
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result SetPermanentLock(Handle handle, const bool permanent_locked) {
|
||||||
|
Event* evt = g_object_pool.GetFast<Event>(handle);
|
||||||
|
_assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
|
||||||
|
|
||||||
|
evt->permanent_locked = permanent_locked;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes whether an event is locked or not
|
||||||
|
* @param handle Handle to event to change
|
||||||
|
* @param locked Boolean locked value to set event
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result SetEventLocked(const Handle handle, const bool locked) {
|
||||||
|
Event* evt = g_object_pool.GetFast<Event>(handle);
|
||||||
|
_assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
|
||||||
|
|
||||||
|
if (!evt->permanent_locked) {
|
||||||
|
evt->locked = locked;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals an event
|
||||||
|
* @param handle Handle to event to signal
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result SignalEvent(const Handle handle) {
|
||||||
|
Event* evt = g_object_pool.GetFast<Event>(handle);
|
||||||
|
_assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
|
||||||
|
|
||||||
|
// Resume threads waiting for event to signal
|
||||||
|
bool event_caught = false;
|
||||||
|
for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
|
||||||
|
ResumeThreadFromWait( evt->waiting_threads[i]);
|
||||||
|
|
||||||
|
// If any thread is signalled awake by this event, assume the event was "caught" and reset
|
||||||
|
// the event. This will result in the next thread waiting on the event to block. Otherwise,
|
||||||
|
// the event will not be reset, and the next thread to call WaitSynchronization on it will
|
||||||
|
// not block. Not sure if this is correct behavior, but it seems to work.
|
||||||
|
event_caught = true;
|
||||||
|
}
|
||||||
|
evt->waiting_threads.clear();
|
||||||
|
|
||||||
|
if (!evt->permanent_locked) {
|
||||||
|
evt->locked = event_caught;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears an event
|
||||||
|
* @param handle Handle to event to clear
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result ClearEvent(Handle handle) {
|
||||||
|
Event* evt = g_object_pool.GetFast<Event>(handle);
|
||||||
|
_assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
|
||||||
|
|
||||||
|
if (!evt->permanent_locked) {
|
||||||
|
evt->locked = true;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event
|
||||||
|
* @param handle Reference to handle for the newly created mutex
|
||||||
|
* @param reset_type ResetType describing how to create event
|
||||||
|
* @param name Optional name of event
|
||||||
|
* @return Newly created Event object
|
||||||
|
*/
|
||||||
|
Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) {
|
||||||
|
Event* evt = new Event;
|
||||||
|
|
||||||
|
handle = Kernel::g_object_pool.Create(evt);
|
||||||
|
|
||||||
|
evt->locked = true;
|
||||||
|
evt->permanent_locked = false;
|
||||||
|
evt->reset_type = evt->intitial_reset_type = reset_type;
|
||||||
|
evt->name = name;
|
||||||
|
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event
|
||||||
|
* @param reset_type ResetType describing how to create event
|
||||||
|
* @param name Optional name of event
|
||||||
|
* @return Handle to newly created Event object
|
||||||
|
*/
|
||||||
|
Handle CreateEvent(const ResetType reset_type, const std::string& name) {
|
||||||
|
Handle handle;
|
||||||
|
Event* evt = CreateEvent(handle, reset_type, name);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/svc.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes whether an event is locked or not
|
||||||
|
* @param handle Handle to event to change
|
||||||
|
* @param locked Boolean locked value to set event
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result SetEventLocked(const Handle handle, const bool locked);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hackish function to set an events permanent lock state, used to pass through synch blocks
|
||||||
|
* @param handle Handle to event to change
|
||||||
|
* @param permanent_locked Boolean permanent locked value to set event
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result SetPermanentLock(Handle handle, const bool permanent_locked);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals an event
|
||||||
|
* @param handle Handle to event to signal
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result SignalEvent(const Handle handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears an event
|
||||||
|
* @param handle Handle to event to clear
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result ClearEvent(Handle handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event
|
||||||
|
* @param reset_type ResetType describing how to create event
|
||||||
|
* @param name Optional name of event
|
||||||
|
* @return Handle to newly created Event object
|
||||||
|
*/
|
||||||
|
Handle CreateEvent(const ResetType reset_type, const std::string& name="Unknown");
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -2,8 +2,6 @@
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
@ -14,6 +12,7 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
Handle g_main_thread = 0;
|
||||||
ObjectPool g_object_pool;
|
ObjectPool g_object_pool;
|
||||||
|
|
||||||
ObjectPool::ObjectPool() {
|
ObjectPool::ObjectPool() {
|
||||||
|
@ -127,16 +126,20 @@ Object* ObjectPool::CreateByIDType(int type) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
|
ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize the kernel
|
||||||
void Init() {
|
void Init() {
|
||||||
Kernel::ThreadingInit();
|
Kernel::ThreadingInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shutdown the kernel
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
Kernel::ThreadingShutdown();
|
Kernel::ThreadingShutdown();
|
||||||
|
|
||||||
|
g_object_pool.Clear(); // Free all kernel objects
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,7 +153,7 @@ bool LoadExec(u32 entry_point) {
|
||||||
Core::g_app_core->SetPC(entry_point);
|
Core::g_app_core->SetPC(entry_point);
|
||||||
|
|
||||||
// 0x30 is the typical main thread priority I've seen used so far
|
// 0x30 is the typical main thread priority I've seen used so far
|
||||||
Handle thread = Kernel::SetupMainThread(0x30);
|
g_main_thread = Kernel::SetupMainThread(0x30);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,11 @@ typedef s32 Result;
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
enum KernelHandle {
|
||||||
|
CurrentThread = 0xFFFF8000,
|
||||||
|
CurrentProcess = 0xFFFF8001,
|
||||||
|
};
|
||||||
|
|
||||||
enum class HandleType : u32 {
|
enum class HandleType : u32 {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
Port = 1,
|
Port = 1,
|
||||||
|
@ -39,9 +44,26 @@ class Object : NonCopyable {
|
||||||
public:
|
public:
|
||||||
virtual ~Object() {}
|
virtual ~Object() {}
|
||||||
Handle GetHandle() const { return handle; }
|
Handle GetHandle() const { return handle; }
|
||||||
virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; }
|
virtual const char* GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; }
|
||||||
virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; }
|
virtual const char* GetName() const { return "[UNKNOWN KERNEL OBJECT]"; }
|
||||||
virtual Kernel::HandleType GetHandleType() const = 0;
|
virtual Kernel::HandleType GetHandleType() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronize kernel object
|
||||||
|
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
virtual Result SyncRequest(bool* wait) {
|
||||||
|
ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for kernel object to synchronize
|
||||||
|
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
virtual Result WaitSynchronization(bool* wait) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ObjectPool : NonCopyable {
|
class ObjectPool : NonCopyable {
|
||||||
|
@ -143,6 +165,13 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ObjectPool g_object_pool;
|
extern ObjectPool g_object_pool;
|
||||||
|
extern Handle g_main_thread;
|
||||||
|
|
||||||
|
/// Initialize the kernel
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
/// Shutdown the kernel
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads executable stored at specified address
|
* Loads executable stored at specified address
|
||||||
|
|
|
@ -8,21 +8,51 @@
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/kernel/mutex.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class Mutex : public Object {
|
class Mutex : public Object {
|
||||||
public:
|
public:
|
||||||
const char* GetTypeName() { return "Mutex"; }
|
const char* GetTypeName() const { return "Mutex"; }
|
||||||
|
const char* GetName() const { return name.c_str(); }
|
||||||
|
|
||||||
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
|
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
|
||||||
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; }
|
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; }
|
||||||
|
|
||||||
bool initial_locked; ///< Initial lock state when mutex was created
|
bool initial_locked; ///< Initial lock state when mutex was created
|
||||||
bool locked; ///< Current locked state
|
bool locked; ///< Current locked state
|
||||||
Handle lock_thread; ///< Handle to thread that currently has mutex
|
Handle lock_thread; ///< Handle to thread that currently has mutex
|
||||||
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
|
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
|
||||||
|
std::string name; ///< Name of mutex (optional)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronize kernel object
|
||||||
|
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result SyncRequest(bool* wait) {
|
||||||
|
// TODO(bunnei): ImplementMe
|
||||||
|
locked = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for kernel object to synchronize
|
||||||
|
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result WaitSynchronization(bool* wait) {
|
||||||
|
// TODO(bunnei): ImplementMe
|
||||||
|
*wait = locked;
|
||||||
|
|
||||||
|
if (locked) {
|
||||||
|
Kernel::WaitCurrentThread(WAITTYPE_MUTEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -70,10 +100,10 @@ bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
|
||||||
bool ReleaseMutex(Mutex* mutex) {
|
bool ReleaseMutex(Mutex* mutex) {
|
||||||
MutexEraseLock(mutex);
|
MutexEraseLock(mutex);
|
||||||
bool woke_threads = false;
|
bool woke_threads = false;
|
||||||
auto iter = mutex->waiting_threads.begin();
|
|
||||||
|
|
||||||
// Find the next waiting thread for the mutex...
|
// Find the next waiting thread for the mutex...
|
||||||
while (!woke_threads && !mutex->waiting_threads.empty()) {
|
while (!woke_threads && !mutex->waiting_threads.empty()) {
|
||||||
|
std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
|
||||||
woke_threads |= ReleaseMutexForThread(mutex, *iter);
|
woke_threads |= ReleaseMutexForThread(mutex, *iter);
|
||||||
mutex->waiting_threads.erase(iter);
|
mutex->waiting_threads.erase(iter);
|
||||||
}
|
}
|
||||||
|
@ -91,6 +121,9 @@ bool ReleaseMutex(Mutex* mutex) {
|
||||||
*/
|
*/
|
||||||
Result ReleaseMutex(Handle handle) {
|
Result ReleaseMutex(Handle handle) {
|
||||||
Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
|
Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
|
||||||
|
|
||||||
|
_assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
|
||||||
|
|
||||||
if (!ReleaseMutex(mutex)) {
|
if (!ReleaseMutex(mutex)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -101,12 +134,15 @@ Result ReleaseMutex(Handle handle) {
|
||||||
* Creates a mutex
|
* Creates a mutex
|
||||||
* @param handle Reference to handle for the newly created mutex
|
* @param handle Reference to handle for the newly created mutex
|
||||||
* @param initial_locked Specifies if the mutex should be locked initially
|
* @param initial_locked Specifies if the mutex should be locked initially
|
||||||
|
* @param name Optional name of mutex
|
||||||
|
* @return Pointer to new Mutex object
|
||||||
*/
|
*/
|
||||||
Mutex* CreateMutex(Handle& handle, bool initial_locked) {
|
Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
|
||||||
Mutex* mutex = new Mutex;
|
Mutex* mutex = new Mutex;
|
||||||
handle = Kernel::g_object_pool.Create(mutex);
|
handle = Kernel::g_object_pool.Create(mutex);
|
||||||
|
|
||||||
mutex->locked = mutex->initial_locked = initial_locked;
|
mutex->locked = mutex->initial_locked = initial_locked;
|
||||||
|
mutex->name = name;
|
||||||
|
|
||||||
// Acquire mutex with current thread if initialized as locked...
|
// Acquire mutex with current thread if initialized as locked...
|
||||||
if (mutex->locked) {
|
if (mutex->locked) {
|
||||||
|
@ -122,10 +158,12 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked) {
|
||||||
/**
|
/**
|
||||||
* Creates a mutex
|
* Creates a mutex
|
||||||
* @param initial_locked Specifies if the mutex should be locked initially
|
* @param initial_locked Specifies if the mutex should be locked initially
|
||||||
|
* @param name Optional name of mutex
|
||||||
|
* @return Handle to newly created object
|
||||||
*/
|
*/
|
||||||
Handle CreateMutex(bool initial_locked) {
|
Handle CreateMutex(bool initial_locked, const std::string& name) {
|
||||||
Handle handle;
|
Handle handle;
|
||||||
Mutex* mutex = CreateMutex(handle, initial_locked);
|
Mutex* mutex = CreateMutex(handle, initial_locked, name);
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,16 @@ namespace Kernel {
|
||||||
/**
|
/**
|
||||||
* Releases a mutex
|
* Releases a mutex
|
||||||
* @param handle Handle to mutex to release
|
* @param handle Handle to mutex to release
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result ReleaseMutex(Handle handle);
|
Result ReleaseMutex(Handle handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a mutex
|
* Creates a mutex
|
||||||
* @param handle Reference to handle for the newly created mutex
|
|
||||||
* @param initial_locked Specifies if the mutex should be locked initially
|
* @param initial_locked Specifies if the mutex should be locked initially
|
||||||
|
* @param name Optional name of mutex
|
||||||
|
* @return Handle to newly created object
|
||||||
*/
|
*/
|
||||||
Handle CreateMutex(bool initial_locked);
|
Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -24,10 +25,10 @@ namespace Kernel {
|
||||||
class Thread : public Kernel::Object {
|
class Thread : public Kernel::Object {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
const char* GetName() { return name; }
|
const char* GetName() const { return name; }
|
||||||
const char* GetTypeName() { return "Thread"; }
|
const char* GetTypeName() const { return "Thread"; }
|
||||||
|
|
||||||
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
|
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
|
||||||
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; }
|
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; }
|
||||||
|
|
||||||
inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
|
inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
|
||||||
|
@ -36,6 +37,23 @@ public:
|
||||||
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
|
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
|
||||||
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
|
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for kernel object to synchronize
|
||||||
|
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result WaitSynchronization(bool* wait) {
|
||||||
|
if (status != THREADSTATUS_DORMANT) {
|
||||||
|
Handle thread = GetCurrentThreadHandle();
|
||||||
|
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
|
||||||
|
waiting_threads.push_back(thread);
|
||||||
|
}
|
||||||
|
WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
|
||||||
|
*wait = true;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ThreadContext context;
|
ThreadContext context;
|
||||||
|
|
||||||
u32 status;
|
u32 status;
|
||||||
|
@ -49,6 +67,9 @@ public:
|
||||||
s32 processor_id;
|
s32 processor_id;
|
||||||
|
|
||||||
WaitType wait_type;
|
WaitType wait_type;
|
||||||
|
Handle wait_handle;
|
||||||
|
|
||||||
|
std::vector<Handle> waiting_threads;
|
||||||
|
|
||||||
char name[Kernel::MAX_NAME_LENGTH + 1];
|
char name[Kernel::MAX_NAME_LENGTH + 1];
|
||||||
};
|
};
|
||||||
|
@ -62,7 +83,6 @@ Common::ThreadQueueList<Handle> g_thread_ready_queue;
|
||||||
Handle g_current_thread_handle;
|
Handle g_current_thread_handle;
|
||||||
Thread* g_current_thread;
|
Thread* g_current_thread;
|
||||||
|
|
||||||
|
|
||||||
/// Gets the current thread
|
/// Gets the current thread
|
||||||
inline Thread* GetCurrentThread() {
|
inline Thread* GetCurrentThread() {
|
||||||
return g_current_thread;
|
return g_current_thread;
|
||||||
|
@ -94,15 +114,15 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
|
||||||
memset(&t->context, 0, sizeof(ThreadContext));
|
memset(&t->context, 0, sizeof(ThreadContext));
|
||||||
|
|
||||||
t->context.cpu_registers[0] = arg;
|
t->context.cpu_registers[0] = arg;
|
||||||
t->context.pc = t->entry_point;
|
t->context.pc = t->context.reg_15 = t->entry_point;
|
||||||
t->context.sp = t->stack_top;
|
t->context.sp = t->stack_top;
|
||||||
t->context.cpsr = 0x1F; // Usermode
|
t->context.cpsr = 0x1F; // Usermode
|
||||||
|
|
||||||
if (t->current_priority < lowest_priority) {
|
if (t->current_priority < lowest_priority) {
|
||||||
t->current_priority = t->initial_priority;
|
t->current_priority = t->initial_priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
t->wait_type = WAITTYPE_NONE;
|
t->wait_type = WAITTYPE_NONE;
|
||||||
|
t->wait_handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change a thread to "ready" state
|
/// Change a thread to "ready" state
|
||||||
|
@ -122,6 +142,37 @@ void ChangeReadyState(Thread* t, bool ready) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify that a thread has not been released from waiting
|
||||||
|
inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
|
||||||
|
Thread* thread = g_object_pool.GetFast<Thread>(handle);
|
||||||
|
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
||||||
|
|
||||||
|
if (type != thread->wait_type || wait_handle != thread->wait_handle)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the current thread
|
||||||
|
void StopThread(Handle handle, const char* reason) {
|
||||||
|
Thread* thread = g_object_pool.GetFast<Thread>(handle);
|
||||||
|
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
||||||
|
|
||||||
|
ChangeReadyState(thread, false);
|
||||||
|
thread->status = THREADSTATUS_DORMANT;
|
||||||
|
for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
|
||||||
|
const Handle waiting_thread = thread->waiting_threads[i];
|
||||||
|
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
|
||||||
|
ResumeThreadFromWait(waiting_thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thread->waiting_threads.clear();
|
||||||
|
|
||||||
|
// Stopped threads are never waiting.
|
||||||
|
thread->wait_type = WAITTYPE_NONE;
|
||||||
|
thread->wait_handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Changes a threads state
|
/// Changes a threads state
|
||||||
void ChangeThreadState(Thread* t, ThreadStatus new_status) {
|
void ChangeThreadState(Thread* t, ThreadStatus new_status) {
|
||||||
if (!t || t->status == new_status) {
|
if (!t || t->status == new_status) {
|
||||||
|
@ -132,7 +183,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
|
||||||
|
|
||||||
if (new_status == THREADSTATUS_WAIT) {
|
if (new_status == THREADSTATUS_WAIT) {
|
||||||
if (t->wait_type == WAITTYPE_NONE) {
|
if (t->wait_type == WAITTYPE_NONE) {
|
||||||
printf("ERROR: Waittype none not allowed here\n");
|
ERROR_LOG(KERNEL, "Waittype none not allowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +217,7 @@ void SwitchContext(Thread* t) {
|
||||||
t->wait_type = WAITTYPE_NONE;
|
t->wait_type = WAITTYPE_NONE;
|
||||||
LoadContext(t->context);
|
LoadContext(t->context);
|
||||||
} else {
|
} else {
|
||||||
SetCurrentThread(NULL);
|
SetCurrentThread(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,26 +232,43 @@ Thread* NextThread() {
|
||||||
next = g_thread_ready_queue.pop_first();
|
next = g_thread_ready_queue.pop_first();
|
||||||
}
|
}
|
||||||
if (next == 0) {
|
if (next == 0) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return Kernel::g_object_pool.GetFast<Thread>(next);
|
return Kernel::g_object_pool.GetFast<Thread>(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Puts the current thread in the wait state for the given type
|
/// Puts the current thread in the wait state for the given type
|
||||||
void WaitCurrentThread(WaitType wait_type) {
|
void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
|
||||||
Thread* t = GetCurrentThread();
|
Thread* thread = GetCurrentThread();
|
||||||
t->wait_type = wait_type;
|
thread->wait_type = wait_type;
|
||||||
ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND)));
|
thread->wait_handle = wait_handle;
|
||||||
|
ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resumes a thread from waiting by marking it as "ready"
|
/// Resumes a thread from waiting by marking it as "ready"
|
||||||
void ResumeThreadFromWait(Handle handle) {
|
void ResumeThreadFromWait(Handle handle) {
|
||||||
u32 error;
|
u32 error;
|
||||||
Thread* t = Kernel::g_object_pool.Get<Thread>(handle, error);
|
Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
|
||||||
if (t) {
|
if (thread) {
|
||||||
t->status &= ~THREADSTATUS_WAIT;
|
thread->status &= ~THREADSTATUS_WAIT;
|
||||||
if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
|
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
|
||||||
ChangeReadyState(t, true);
|
ChangeReadyState(thread, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints the thread queue for debugging purposes
|
||||||
|
void DebugThreadQueue() {
|
||||||
|
Thread* thread = GetCurrentThread();
|
||||||
|
if (!thread) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
|
||||||
|
for (u32 i = 0; i < g_thread_queue.size(); i++) {
|
||||||
|
Handle handle = g_thread_queue[i];
|
||||||
|
s32 priority = g_thread_ready_queue.contains(handle);
|
||||||
|
if (priority != -1) {
|
||||||
|
INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,32 +280,34 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
|
||||||
_assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
|
_assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
|
||||||
"CreateThread priority=%d, outside of allowable range!", priority)
|
"CreateThread priority=%d, outside of allowable range!", priority)
|
||||||
|
|
||||||
Thread* t = new Thread;
|
Thread* thread = new Thread;
|
||||||
|
|
||||||
handle = Kernel::g_object_pool.Create(t);
|
handle = Kernel::g_object_pool.Create(thread);
|
||||||
|
|
||||||
g_thread_queue.push_back(handle);
|
g_thread_queue.push_back(handle);
|
||||||
g_thread_ready_queue.prepare(priority);
|
g_thread_ready_queue.prepare(priority);
|
||||||
|
|
||||||
t->status = THREADSTATUS_DORMANT;
|
thread->status = THREADSTATUS_DORMANT;
|
||||||
t->entry_point = entry_point;
|
thread->entry_point = entry_point;
|
||||||
t->stack_top = stack_top;
|
thread->stack_top = stack_top;
|
||||||
t->stack_size = stack_size;
|
thread->stack_size = stack_size;
|
||||||
t->initial_priority = t->current_priority = priority;
|
thread->initial_priority = thread->current_priority = priority;
|
||||||
t->processor_id = processor_id;
|
thread->processor_id = processor_id;
|
||||||
t->wait_type = WAITTYPE_NONE;
|
thread->wait_type = WAITTYPE_NONE;
|
||||||
|
thread->wait_handle = 0;
|
||||||
|
|
||||||
strncpy(t->name, name, Kernel::MAX_NAME_LENGTH);
|
strncpy(thread->name, name, Kernel::MAX_NAME_LENGTH);
|
||||||
t->name[Kernel::MAX_NAME_LENGTH] = '\0';
|
thread->name[Kernel::MAX_NAME_LENGTH] = '\0';
|
||||||
|
|
||||||
return t;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new thread - wrapper for external user
|
/// Creates a new thread - wrapper for external user
|
||||||
Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
|
Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
|
||||||
u32 stack_top, int stack_size) {
|
u32 stack_top, int stack_size) {
|
||||||
if (name == NULL) {
|
|
||||||
ERROR_LOG(KERNEL, "CreateThread(): NULL name");
|
if (name == nullptr) {
|
||||||
|
ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if ((u32)stack_size < 0x200) {
|
if ((u32)stack_size < 0x200) {
|
||||||
|
@ -258,31 +328,67 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Handle handle;
|
Handle handle;
|
||||||
Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
|
Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
|
||||||
stack_size);
|
stack_size);
|
||||||
|
|
||||||
ResetThread(t, arg, 0);
|
ResetThread(thread, arg, 0);
|
||||||
|
CallThread(thread);
|
||||||
HLE::EatCycles(32000);
|
|
||||||
|
|
||||||
// This won't schedule to the new thread, but it may to one woken from eating cycles.
|
|
||||||
// Technically, this should not eat all at once, and reschedule in the middle, but that's hard.
|
|
||||||
HLE::ReSchedule("thread created");
|
|
||||||
|
|
||||||
CallThread(t);
|
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the priority of the thread specified by handle
|
||||||
|
u32 GetThreadPriority(const Handle handle) {
|
||||||
|
Thread* thread = g_object_pool.GetFast<Thread>(handle);
|
||||||
|
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
||||||
|
return thread->current_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the priority of the thread specified by handle
|
||||||
|
Result SetThreadPriority(Handle handle, s32 priority) {
|
||||||
|
Thread* thread = nullptr;
|
||||||
|
if (!handle) {
|
||||||
|
thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
|
||||||
|
} else {
|
||||||
|
thread = g_object_pool.GetFast<Thread>(handle);
|
||||||
|
}
|
||||||
|
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
||||||
|
|
||||||
|
// If priority is invalid, clamp to valid range
|
||||||
|
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
|
||||||
|
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
|
||||||
|
WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
|
||||||
|
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
|
||||||
|
// validity of this
|
||||||
|
priority = new_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change thread priority
|
||||||
|
s32 old = thread->current_priority;
|
||||||
|
g_thread_ready_queue.remove(old, handle);
|
||||||
|
thread->current_priority = priority;
|
||||||
|
g_thread_ready_queue.prepare(thread->current_priority);
|
||||||
|
|
||||||
|
// Change thread status to "ready" and push to ready queue
|
||||||
|
if (thread->IsRunning()) {
|
||||||
|
thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
|
||||||
|
}
|
||||||
|
if (thread->IsReady()) {
|
||||||
|
g_thread_ready_queue.push_back(thread->current_priority, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets up the primary application thread
|
/// Sets up the primary application thread
|
||||||
Handle SetupMainThread(s32 priority, int stack_size) {
|
Handle SetupMainThread(s32 priority, int stack_size) {
|
||||||
Handle handle;
|
Handle handle;
|
||||||
|
|
||||||
// Initialize new "main" thread
|
// Initialize new "main" thread
|
||||||
Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
|
Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
|
||||||
THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
|
THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
|
||||||
|
|
||||||
ResetThread(t, 0, 0);
|
ResetThread(thread, 0, 0);
|
||||||
|
|
||||||
// If running another thread already, set it to "ready" state
|
// If running another thread already, set it to "ready" state
|
||||||
Thread* cur = GetCurrentThread();
|
Thread* cur = GetCurrentThread();
|
||||||
|
@ -291,24 +397,31 @@ Handle SetupMainThread(s32 priority, int stack_size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run new "main" thread
|
// Run new "main" thread
|
||||||
SetCurrentThread(t);
|
SetCurrentThread(thread);
|
||||||
t->status = THREADSTATUS_RUNNING;
|
thread->status = THREADSTATUS_RUNNING;
|
||||||
LoadContext(t->context);
|
LoadContext(thread->context);
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||||
void Reschedule() {
|
void Reschedule() {
|
||||||
Thread* prev = GetCurrentThread();
|
Thread* prev = GetCurrentThread();
|
||||||
Thread* next = NextThread();
|
Thread* next = NextThread();
|
||||||
|
HLE::g_reschedule = false;
|
||||||
if (next > 0) {
|
if (next > 0) {
|
||||||
|
INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
|
||||||
|
|
||||||
SwitchContext(next);
|
SwitchContext(next);
|
||||||
|
|
||||||
// Hack - automatically change previous thread (which would have been in "wait" state) to
|
// Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
|
||||||
// "ready" state, so that we can immediately resume to it when new thread yields. FixMe to
|
// by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
|
||||||
// actually wait for whatever event it is supposed to be waiting on.
|
// This results in the current thread yielding on a VBLANK once, and then it will be
|
||||||
ChangeReadyState(prev, true);
|
// immediately placed back in the queue for execution.
|
||||||
|
if (prev->wait_type == WAITTYPE_VBLANK) {
|
||||||
|
ResumeThreadFromWait(prev->GetHandle());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ enum WaitType {
|
||||||
WAITTYPE_NONE,
|
WAITTYPE_NONE,
|
||||||
WAITTYPE_SLEEP,
|
WAITTYPE_SLEEP,
|
||||||
WAITTYPE_SEMA,
|
WAITTYPE_SEMA,
|
||||||
WAITTYPE_EVENTFLAG,
|
WAITTYPE_EVENT,
|
||||||
WAITTYPE_THREADEND,
|
WAITTYPE_THREADEND,
|
||||||
WAITTYPE_VBLANK,
|
WAITTYPE_VBLANK,
|
||||||
WAITTYPE_MUTEX,
|
WAITTYPE_MUTEX,
|
||||||
|
@ -53,8 +53,8 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
|
||||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||||
void Reschedule();
|
void Reschedule();
|
||||||
|
|
||||||
/// Puts the current thread in the wait state for the given type
|
/// Stops the current thread
|
||||||
void WaitCurrentThread(WaitType wait_type);
|
void StopThread(Handle thread, const char* reason);
|
||||||
|
|
||||||
/// Resumes a thread from waiting by marking it as "ready"
|
/// Resumes a thread from waiting by marking it as "ready"
|
||||||
void ResumeThreadFromWait(Handle handle);
|
void ResumeThreadFromWait(Handle handle);
|
||||||
|
@ -62,9 +62,18 @@ void ResumeThreadFromWait(Handle handle);
|
||||||
/// Gets the current thread handle
|
/// Gets the current thread handle
|
||||||
Handle GetCurrentThreadHandle();
|
Handle GetCurrentThreadHandle();
|
||||||
|
|
||||||
|
/// Puts the current thread in the wait state for the given type
|
||||||
|
void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
|
||||||
|
|
||||||
/// Put current thread in a wait state - on WaitSynchronization
|
/// Put current thread in a wait state - on WaitSynchronization
|
||||||
void WaitThread_Synchronization();
|
void WaitThread_Synchronization();
|
||||||
|
|
||||||
|
/// Get the priority of the thread specified by handle
|
||||||
|
u32 GetThreadPriority(const Handle handle);
|
||||||
|
|
||||||
|
/// Set the priority of the thread specified by handle
|
||||||
|
Result SetThreadPriority(Handle handle, s32 priority);
|
||||||
|
|
||||||
/// Initialize threading
|
/// Initialize threading
|
||||||
void ThreadingInit();
|
void ThreadingInit();
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
|
#include "core/hle/kernel/event.h"
|
||||||
#include "core/hle/kernel/mutex.h"
|
#include "core/hle/kernel/mutex.h"
|
||||||
#include "core/hle/service/apt.h"
|
#include "core/hle/service/apt.h"
|
||||||
|
|
||||||
|
@ -15,96 +16,120 @@
|
||||||
namespace APT_U {
|
namespace APT_U {
|
||||||
|
|
||||||
void Initialize(Service::Interface* self) {
|
void Initialize(Service::Interface* self) {
|
||||||
NOTICE_LOG(OSHLE, "APT_U::Sync - Initialize");
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
|
DEBUG_LOG(KERNEL, "called");
|
||||||
|
|
||||||
|
cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
|
||||||
|
cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
|
||||||
|
|
||||||
|
Kernel::SetEventLocked(cmd_buff[3], true);
|
||||||
|
Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
|
||||||
|
|
||||||
|
cmd_buff[1] = 0; // No error
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetLockHandle(Service::Interface* self) {
|
void GetLockHandle(Service::Interface* self) {
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
|
u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
|
||||||
cmd_buff[1] = 0; // No error
|
cmd_buff[1] = 0; // No error
|
||||||
cmd_buff[5] = Kernel::CreateMutex(false);
|
cmd_buff[5] = Kernel::CreateMutex(false, "APT_U:Lock");
|
||||||
DEBUG_LOG(KERNEL, "APT_U::GetLockHandle called : created handle 0x%08X", cmd_buff[5]);
|
DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enable(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
|
u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
|
||||||
|
cmd_buff[1] = 0; // No error
|
||||||
|
ERROR_LOG(KERNEL, "(UNIMPEMENTED) called unk=0x%08X", unk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InquireNotification(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
|
u32 app_id = cmd_buff[2];
|
||||||
|
cmd_buff[1] = 0; // No error
|
||||||
|
cmd_buff[3] = 0; // Signal type
|
||||||
|
ERROR_LOG(KERNEL, "(UNIMPEMENTED) called app_id=0x%08X", app_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Interface::FunctionInfo FunctionTable[] = {
|
const Interface::FunctionInfo FunctionTable[] = {
|
||||||
{0x00010040, GetLockHandle, "GetLockHandle"},
|
{0x00010040, GetLockHandle, "GetLockHandle"},
|
||||||
{0x00020080, Initialize, "Initialize"},
|
{0x00020080, Initialize, "Initialize"},
|
||||||
{0x00030040, NULL, "Enable"},
|
{0x00030040, Enable, "Enable"},
|
||||||
{0x00040040, NULL, "Finalize"},
|
{0x00040040, nullptr, "Finalize"},
|
||||||
{0x00050040, NULL, "GetAppletManInfo"},
|
{0x00050040, nullptr, "GetAppletManInfo"},
|
||||||
{0x00060040, NULL, "GetAppletInfo"},
|
{0x00060040, nullptr, "GetAppletInfo"},
|
||||||
{0x00070000, NULL, "GetLastSignaledAppletId"},
|
{0x00070000, nullptr, "GetLastSignaledAppletId"},
|
||||||
{0x00080000, NULL, "CountRegisteredApplet"},
|
{0x00080000, nullptr, "CountRegisteredApplet"},
|
||||||
{0x00090040, NULL, "IsRegistered"},
|
{0x00090040, nullptr, "IsRegistered"},
|
||||||
{0x000A0040, NULL, "GetAttribute"},
|
{0x000A0040, nullptr, "GetAttribute"},
|
||||||
{0x000B0040, NULL, "InquireNotification"},
|
{0x000B0040, InquireNotification, "InquireNotification"},
|
||||||
{0x000C0104, NULL, "SendParameter"},
|
{0x000C0104, nullptr, "SendParameter"},
|
||||||
{0x000D0080, NULL, "ReceiveParameter"},
|
{0x000D0080, nullptr, "ReceiveParameter"},
|
||||||
{0x000E0080, NULL, "GlanceParameter"},
|
{0x000E0080, nullptr, "GlanceParameter"},
|
||||||
{0x000F0100, NULL, "CancelParameter"},
|
{0x000F0100, nullptr, "CancelParameter"},
|
||||||
{0x001000C2, NULL, "DebugFunc"},
|
{0x001000C2, nullptr, "DebugFunc"},
|
||||||
{0x001100C0, NULL, "MapProgramIdForDebug"},
|
{0x001100C0, nullptr, "MapProgramIdForDebug"},
|
||||||
{0x00120040, NULL, "SetHomeMenuAppletIdForDebug"},
|
{0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
|
||||||
{0x00130000, NULL, "GetPreparationState"},
|
{0x00130000, nullptr, "GetPreparationState"},
|
||||||
{0x00140040, NULL, "SetPreparationState"},
|
{0x00140040, nullptr, "SetPreparationState"},
|
||||||
{0x00150140, NULL, "PrepareToStartApplication"},
|
{0x00150140, nullptr, "PrepareToStartApplication"},
|
||||||
{0x00160040, NULL, "PreloadLibraryApplet"},
|
{0x00160040, nullptr, "PreloadLibraryApplet"},
|
||||||
{0x00170040, NULL, "FinishPreloadingLibraryApplet"},
|
{0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
|
||||||
{0x00180040, NULL, "PrepareToStartLibraryApplet"},
|
{0x00180040, nullptr, "PrepareToStartLibraryApplet"},
|
||||||
{0x00190040, NULL, "PrepareToStartSystemApplet"},
|
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
|
||||||
{0x001A0000, NULL, "PrepareToStartNewestHomeMenu"},
|
{0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
|
||||||
{0x001B00C4, NULL, "StartApplication"},
|
{0x001B00C4, nullptr, "StartApplication"},
|
||||||
{0x001C0000, NULL, "WakeupApplication"},
|
{0x001C0000, nullptr, "WakeupApplication"},
|
||||||
{0x001D0000, NULL, "CancelApplication"},
|
{0x001D0000, nullptr, "CancelApplication"},
|
||||||
{0x001E0084, NULL, "StartLibraryApplet"},
|
{0x001E0084, nullptr, "StartLibraryApplet"},
|
||||||
{0x001F0084, NULL, "StartSystemApplet"},
|
{0x001F0084, nullptr, "StartSystemApplet"},
|
||||||
{0x00200044, NULL, "StartNewestHomeMenu"},
|
{0x00200044, nullptr, "StartNewestHomeMenu"},
|
||||||
{0x00210000, NULL, "OrderToCloseApplication"},
|
{0x00210000, nullptr, "OrderToCloseApplication"},
|
||||||
{0x00220040, NULL, "PrepareToCloseApplication"},
|
{0x00220040, nullptr, "PrepareToCloseApplication"},
|
||||||
{0x00230040, NULL, "PrepareToJumpToApplication"},
|
{0x00230040, nullptr, "PrepareToJumpToApplication"},
|
||||||
{0x00240044, NULL, "JumpToApplication"},
|
{0x00240044, nullptr, "JumpToApplication"},
|
||||||
{0x002500C0, NULL, "PrepareToCloseLibraryApplet"},
|
{0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
|
||||||
{0x00260000, NULL, "PrepareToCloseSystemApplet"},
|
{0x00260000, nullptr, "PrepareToCloseSystemApplet"},
|
||||||
{0x00270044, NULL, "CloseApplication"},
|
{0x00270044, nullptr, "CloseApplication"},
|
||||||
{0x00280044, NULL, "CloseLibraryApplet"},
|
{0x00280044, nullptr, "CloseLibraryApplet"},
|
||||||
{0x00290044, NULL, "CloseSystemApplet"},
|
{0x00290044, nullptr, "CloseSystemApplet"},
|
||||||
{0x002A0000, NULL, "OrderToCloseSystemApplet"},
|
{0x002A0000, nullptr, "OrderToCloseSystemApplet"},
|
||||||
{0x002B0000, NULL, "PrepareToJumpToHomeMenu"},
|
{0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
|
||||||
{0x002C0044, NULL, "JumpToHomeMenu"},
|
{0x002C0044, nullptr, "JumpToHomeMenu"},
|
||||||
{0x002D0000, NULL, "PrepareToLeaveHomeMenu"},
|
{0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
|
||||||
{0x002E0044, NULL, "LeaveHomeMenu"},
|
{0x002E0044, nullptr, "LeaveHomeMenu"},
|
||||||
{0x002F0040, NULL, "PrepareToLeaveResidentApplet"},
|
{0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
|
||||||
{0x00300044, NULL, "LeaveResidentApplet"},
|
{0x00300044, nullptr, "LeaveResidentApplet"},
|
||||||
{0x00310100, NULL, "PrepareToDoApplicationJump"},
|
{0x00310100, nullptr, "PrepareToDoApplicationJump"},
|
||||||
{0x00320084, NULL, "DoApplicationJump"},
|
{0x00320084, nullptr, "DoApplicationJump"},
|
||||||
{0x00330000, NULL, "GetProgramIdOnApplicationJump"},
|
{0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
|
||||||
{0x00340084, NULL, "SendDeliverArg"},
|
{0x00340084, nullptr, "SendDeliverArg"},
|
||||||
{0x00350080, NULL, "ReceiveDeliverArg"},
|
{0x00350080, nullptr, "ReceiveDeliverArg"},
|
||||||
{0x00360040, NULL, "LoadSysMenuArg"},
|
{0x00360040, nullptr, "LoadSysMenuArg"},
|
||||||
{0x00370042, NULL, "StoreSysMenuArg"},
|
{0x00370042, nullptr, "StoreSysMenuArg"},
|
||||||
{0x00380040, NULL, "PreloadResidentApplet"},
|
{0x00380040, nullptr, "PreloadResidentApplet"},
|
||||||
{0x00390040, NULL, "PrepareToStartResidentApplet"},
|
{0x00390040, nullptr, "PrepareToStartResidentApplet"},
|
||||||
{0x003A0044, NULL, "StartResidentApplet"},
|
{0x003A0044, nullptr, "StartResidentApplet"},
|
||||||
{0x003B0040, NULL, "CancelLibraryApplet"},
|
{0x003B0040, nullptr, "CancelLibraryApplet"},
|
||||||
{0x003C0042, NULL, "SendDspSleep"},
|
{0x003C0042, nullptr, "SendDspSleep"},
|
||||||
{0x003D0042, NULL, "SendDspWakeUp"},
|
{0x003D0042, nullptr, "SendDspWakeUp"},
|
||||||
{0x003E0080, NULL, "ReplySleepQuery"},
|
{0x003E0080, nullptr, "ReplySleepQuery"},
|
||||||
{0x003F0040, NULL, "ReplySleepNotificationComplete"},
|
{0x003F0040, nullptr, "ReplySleepNotificationComplete"},
|
||||||
{0x00400042, NULL, "SendCaptureBufferInfo"},
|
{0x00400042, nullptr, "SendCaptureBufferInfo"},
|
||||||
{0x00410040, NULL, "ReceiveCaptureBufferInfo"},
|
{0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
|
||||||
{0x00420080, NULL, "SleepSystem"},
|
{0x00420080, nullptr, "SleepSystem"},
|
||||||
{0x00430040, NULL, "NotifyToWait"},
|
{0x00430040, nullptr, "NotifyToWait"},
|
||||||
{0x00440000, NULL, "GetSharedFont"},
|
{0x00440000, nullptr, "GetSharedFont"},
|
||||||
{0x00450040, NULL, "GetWirelessRebootInfo"},
|
{0x00450040, nullptr, "GetWirelessRebootInfo"},
|
||||||
{0x00460104, NULL, "Wrap"},
|
{0x00460104, nullptr, "Wrap"},
|
||||||
{0x00470104, NULL, "Unwrap"},
|
{0x00470104, nullptr, "Unwrap"},
|
||||||
{0x00480100, NULL, "GetProgramInfo"},
|
{0x00480100, nullptr, "GetProgramInfo"},
|
||||||
{0x00490180, NULL, "Reboot"},
|
{0x00490180, nullptr, "Reboot"},
|
||||||
{0x004A0040, NULL, "GetCaptureInfo"},
|
{0x004A0040, nullptr, "GetCaptureInfo"},
|
||||||
{0x004B00C2, NULL, "AppletUtility"},
|
{0x004B00C2, nullptr, "AppletUtility"},
|
||||||
{0x004C0000, NULL, "SetFatalErrDispMode"},
|
{0x004C0000, nullptr, "SetFatalErrDispMode"},
|
||||||
{0x004D0080, NULL, "GetAppletProgramInfo"},
|
{0x004D0080, nullptr, "GetAppletProgramInfo"},
|
||||||
{0x004E0000, NULL, "HardwareResetAsync"},
|
{0x004E0000, nullptr, "HardwareResetAsync"},
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "core/mem_map.h"
|
#include "core/mem_map.h"
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
|
#include "core/hle/kernel/event.h"
|
||||||
#include "core/hle/service/gsp.h"
|
#include "core/hle/service/gsp.h"
|
||||||
|
|
||||||
#include "core/hw/gpu.h"
|
#include "core/hw/gpu.h"
|
||||||
|
@ -60,6 +61,7 @@ void GX_FinishCommand(u32 thread_id) {
|
||||||
|
|
||||||
namespace GSP_GPU {
|
namespace GSP_GPU {
|
||||||
|
|
||||||
|
Handle g_event_handle = 0;
|
||||||
u32 g_thread_id = 0;
|
u32 g_thread_id = 0;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -96,7 +98,7 @@ void ReadHWRegs(Service::Interface* self) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(GSP, "ReadHWRegs unknown register read at address %08X", reg_addr);
|
ERROR_LOG(GSP, "unknown register read at address %08X", reg_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -104,7 +106,19 @@ void ReadHWRegs(Service::Interface* self) {
|
||||||
void RegisterInterruptRelayQueue(Service::Interface* self) {
|
void RegisterInterruptRelayQueue(Service::Interface* self) {
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
u32 flags = cmd_buff[1];
|
u32 flags = cmd_buff[1];
|
||||||
u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling
|
u32 event_handle = cmd_buff[3];
|
||||||
|
|
||||||
|
_assert_msg_(GSP, (event_handle != 0), "called, but event is nullptr!");
|
||||||
|
|
||||||
|
g_event_handle = event_handle;
|
||||||
|
|
||||||
|
Kernel::SetEventLocked(event_handle, false);
|
||||||
|
|
||||||
|
// Hack - This function will permanently set the state of the GSP event such that GPU command
|
||||||
|
// synchronization barriers always passthrough. Correct solution would be to set this after the
|
||||||
|
// GPU as processed all queued up commands, but due to the emulator being single-threaded they
|
||||||
|
// will always be ready.
|
||||||
|
Kernel::SetPermanentLock(event_handle, true);
|
||||||
|
|
||||||
cmd_buff[2] = g_thread_id; // ThreadID
|
cmd_buff[2] = g_thread_id; // ThreadID
|
||||||
}
|
}
|
||||||
|
@ -150,43 +164,43 @@ void TriggerCmdReqQueue(Service::Interface* self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(GSP, "TriggerCmdReqQueue unknown command 0x%08X", cmd_buff[0]);
|
ERROR_LOG(GSP, "unknown command 0x%08X", cmd_buff[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
GX_FinishCommand(g_thread_id);
|
GX_FinishCommand(g_thread_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Interface::FunctionInfo FunctionTable[] = {
|
const Interface::FunctionInfo FunctionTable[] = {
|
||||||
{0x00010082, NULL, "WriteHWRegs"},
|
{0x00010082, nullptr, "WriteHWRegs"},
|
||||||
{0x00020084, NULL, "WriteHWRegsWithMask"},
|
{0x00020084, nullptr, "WriteHWRegsWithMask"},
|
||||||
{0x00030082, NULL, "WriteHWRegRepeat"},
|
{0x00030082, nullptr, "WriteHWRegRepeat"},
|
||||||
{0x00040080, ReadHWRegs, "ReadHWRegs"},
|
{0x00040080, ReadHWRegs, "ReadHWRegs"},
|
||||||
{0x00050200, NULL, "SetBufferSwap"},
|
{0x00050200, nullptr, "SetBufferSwap"},
|
||||||
{0x00060082, NULL, "SetCommandList"},
|
{0x00060082, nullptr, "SetCommandList"},
|
||||||
{0x000700C2, NULL, "RequestDma"},
|
{0x000700C2, nullptr, "RequestDma"},
|
||||||
{0x00080082, NULL, "FlushDataCache"},
|
{0x00080082, nullptr, "FlushDataCache"},
|
||||||
{0x00090082, NULL, "InvalidateDataCache"},
|
{0x00090082, nullptr, "InvalidateDataCache"},
|
||||||
{0x000A0044, NULL, "RegisterInterruptEvents"},
|
{0x000A0044, nullptr, "RegisterInterruptEvents"},
|
||||||
{0x000B0040, NULL, "SetLcdForceBlack"},
|
{0x000B0040, nullptr, "SetLcdForceBlack"},
|
||||||
{0x000C0000, TriggerCmdReqQueue, "TriggerCmdReqQueue"},
|
{0x000C0000, TriggerCmdReqQueue, "TriggerCmdReqQueue"},
|
||||||
{0x000D0140, NULL, "SetDisplayTransfer"},
|
{0x000D0140, nullptr, "SetDisplayTransfer"},
|
||||||
{0x000E0180, NULL, "SetTextureCopy"},
|
{0x000E0180, nullptr, "SetTextureCopy"},
|
||||||
{0x000F0200, NULL, "SetMemoryFill"},
|
{0x000F0200, nullptr, "SetMemoryFill"},
|
||||||
{0x00100040, NULL, "SetAxiConfigQoSMode"},
|
{0x00100040, nullptr, "SetAxiConfigQoSMode"},
|
||||||
{0x00110040, NULL, "SetPerfLogMode"},
|
{0x00110040, nullptr, "SetPerfLogMode"},
|
||||||
{0x00120000, NULL, "GetPerfLog"},
|
{0x00120000, nullptr, "GetPerfLog"},
|
||||||
{0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"},
|
{0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"},
|
||||||
{0x00140000, NULL, "UnregisterInterruptRelayQueue"},
|
{0x00140000, nullptr, "UnregisterInterruptRelayQueue"},
|
||||||
{0x00150002, NULL, "TryAcquireRight"},
|
{0x00150002, nullptr, "TryAcquireRight"},
|
||||||
{0x00160042, NULL, "AcquireRight"},
|
{0x00160042, nullptr, "AcquireRight"},
|
||||||
{0x00170000, NULL, "ReleaseRight"},
|
{0x00170000, nullptr, "ReleaseRight"},
|
||||||
{0x00180000, NULL, "ImportDisplayCaptureInfo"},
|
{0x00180000, nullptr, "ImportDisplayCaptureInfo"},
|
||||||
{0x00190000, NULL, "SaveVramSysArea"},
|
{0x00190000, nullptr, "SaveVramSysArea"},
|
||||||
{0x001A0000, NULL, "RestoreVramSysArea"},
|
{0x001A0000, nullptr, "RestoreVramSysArea"},
|
||||||
{0x001B0000, NULL, "ResetGpuCore"},
|
{0x001B0000, nullptr, "ResetGpuCore"},
|
||||||
{0x001C0040, NULL, "SetLedForceOff"},
|
{0x001C0040, nullptr, "SetLedForceOff"},
|
||||||
{0x001D0040, NULL, "SetTestCommand"},
|
{0x001D0040, nullptr, "SetTestCommand"},
|
||||||
{0x001E0080, NULL, "SetInternalPriorities"},
|
{0x001E0080, nullptr, "SetInternalPriorities"},
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -13,11 +13,11 @@
|
||||||
namespace HID_User {
|
namespace HID_User {
|
||||||
|
|
||||||
const Interface::FunctionInfo FunctionTable[] = {
|
const Interface::FunctionInfo FunctionTable[] = {
|
||||||
{0x000A0000, NULL, "GetIPCHandles"},
|
{0x000A0000, nullptr, "GetIPCHandles"},
|
||||||
{0x00110000, NULL, "EnableAccelerometer"},
|
{0x00110000, nullptr, "EnableAccelerometer"},
|
||||||
{0x00130000, NULL, "EnableGyroscopeLow"},
|
{0x00130000, nullptr, "EnableGyroscopeLow"},
|
||||||
{0x00150000, NULL, "GetGyroscopeLowRawToDpsCoefficient"},
|
{0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
|
||||||
{0x00160000, NULL, "GetGyroscopeLowCalibrateParam"},
|
{0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/log.h"
|
||||||
|
|
||||||
|
#include "core/hle/hle.h"
|
||||||
|
#include "core/hle/service/ndm.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Namespace NDM_U
|
||||||
|
|
||||||
|
namespace NDM_U {
|
||||||
|
|
||||||
|
const Interface::FunctionInfo FunctionTable[] = {
|
||||||
|
{0x00060040, nullptr, "SuspendDaemons"},
|
||||||
|
{0x00080040, nullptr, "DisableWifiUsage"},
|
||||||
|
{0x00090000, nullptr, "EnableWifiUsage"},
|
||||||
|
{0x00140040, nullptr, "OverrideDefaultDaemons"},
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Interface class
|
||||||
|
|
||||||
|
Interface::Interface() {
|
||||||
|
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
|
||||||
|
}
|
||||||
|
|
||||||
|
Interface::~Interface() {
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Namespace NDM
|
||||||
|
|
||||||
|
// No idea what this is
|
||||||
|
|
||||||
|
namespace NDM_U {
|
||||||
|
|
||||||
|
class Interface : public Service::Interface {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Interface();
|
||||||
|
|
||||||
|
~Interface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string port name used by CTROS for the service
|
||||||
|
* @return Port name of service
|
||||||
|
*/
|
||||||
|
const char *GetPortName() const {
|
||||||
|
return "ndm:u";
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -12,13 +12,14 @@
|
||||||
#include "core/hle/service/apt.h"
|
#include "core/hle/service/apt.h"
|
||||||
#include "core/hle/service/gsp.h"
|
#include "core/hle/service/gsp.h"
|
||||||
#include "core/hle/service/hid.h"
|
#include "core/hle/service/hid.h"
|
||||||
|
#include "core/hle/service/ndm.h"
|
||||||
#include "core/hle/service/srv.h"
|
#include "core/hle/service/srv.h"
|
||||||
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
Manager* g_manager = NULL; ///< Service manager
|
Manager* g_manager = nullptr; ///< Service manager
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Service Manager class
|
// Service Manager class
|
||||||
|
@ -55,7 +56,7 @@ Interface* Manager::FetchFromHandle(Handle handle) {
|
||||||
Interface* Manager::FetchFromPortName(std::string port_name) {
|
Interface* Manager::FetchFromPortName(std::string port_name) {
|
||||||
auto itr = m_port_map.find(port_name);
|
auto itr = m_port_map.find(port_name);
|
||||||
if (itr == m_port_map.end()) {
|
if (itr == m_port_map.end()) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return FetchFromHandle(itr->second);
|
return FetchFromHandle(itr->second);
|
||||||
}
|
}
|
||||||
|
@ -72,14 +73,15 @@ void Init() {
|
||||||
g_manager->AddService(new APT_U::Interface);
|
g_manager->AddService(new APT_U::Interface);
|
||||||
g_manager->AddService(new GSP_GPU::Interface);
|
g_manager->AddService(new GSP_GPU::Interface);
|
||||||
g_manager->AddService(new HID_User::Interface);
|
g_manager->AddService(new HID_User::Interface);
|
||||||
|
g_manager->AddService(new NDM_U::Interface);
|
||||||
|
|
||||||
NOTICE_LOG(HLE, "Services initialized OK");
|
NOTICE_LOG(HLE, "initialized OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shutdown ServiceManager
|
/// Shutdown ServiceManager
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
delete g_manager;
|
delete g_manager;
|
||||||
NOTICE_LOG(HLE, "Services shutdown OK");
|
NOTICE_LOG(HLE, "shutdown OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,8 @@ class Interface : public Kernel::Object {
|
||||||
friend class Manager;
|
friend class Manager;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
const char *GetName() { return GetPortName(); }
|
const char *GetName() const { return GetPortName(); }
|
||||||
const char *GetTypeName() { return GetPortName(); }
|
const char *GetTypeName() const { return GetPortName(); }
|
||||||
|
|
||||||
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; }
|
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; }
|
||||||
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Service; }
|
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Service; }
|
||||||
|
@ -76,22 +76,31 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when svcSendSyncRequest is called, loads command buffer and executes comand
|
* Synchronize kernel object
|
||||||
* @return Return result of svcSendSyncRequest passed back to user app
|
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result Sync() {
|
Result SyncRequest(bool* wait) {
|
||||||
u32* cmd_buff = GetCommandBuffer();
|
u32* cmd_buff = GetCommandBuffer();
|
||||||
auto itr = m_functions.find(cmd_buff[0]);
|
auto itr = m_functions.find(cmd_buff[0]);
|
||||||
|
|
||||||
if (itr == m_functions.end()) {
|
if (itr == m_functions.end()) {
|
||||||
ERROR_LOG(OSHLE, "Unknown/unimplemented function: port = %s, command = 0x%08X!",
|
ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X",
|
||||||
GetPortName(), cmd_buff[0]);
|
GetPortName(), cmd_buff[0]);
|
||||||
return -1;
|
|
||||||
|
// TODO(bunnei): Hack - ignore error
|
||||||
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
|
cmd_buff[1] = 0;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
if (itr->second.func == NULL) {
|
if (itr->second.func == nullptr) {
|
||||||
ERROR_LOG(OSHLE, "Unimplemented function: port = %s, name = %s!",
|
ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s",
|
||||||
GetPortName(), itr->second.name.c_str());
|
GetPortName(), itr->second.name.c_str());
|
||||||
return -1;
|
|
||||||
|
// TODO(bunnei): Hack - ignore error
|
||||||
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
|
cmd_buff[1] = 0;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
itr->second.func(this);
|
itr->second.func(this);
|
||||||
|
@ -99,6 +108,17 @@ public:
|
||||||
return 0; // TODO: Implement return from actual function
|
return 0; // TODO: Implement return from actual function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for kernel object to synchronize
|
||||||
|
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
||||||
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
Result WaitSynchronization(bool* wait) {
|
||||||
|
// TODO(bunnei): ImplementMe
|
||||||
|
ERROR_LOG(OSHLE, "unimplemented function");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,21 +5,28 @@
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
#include "core/hle/service/srv.h"
|
#include "core/hle/service/srv.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
#include "core/hle/kernel/mutex.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Namespace SRV
|
// Namespace SRV
|
||||||
|
|
||||||
namespace SRV {
|
namespace SRV {
|
||||||
|
|
||||||
|
Handle g_mutex = 0;
|
||||||
|
|
||||||
void Initialize(Service::Interface* self) {
|
void Initialize(Service::Interface* self) {
|
||||||
NOTICE_LOG(OSHLE, "SRV::Sync - Initialize");
|
DEBUG_LOG(OSHLE, "called");
|
||||||
|
if (!g_mutex) {
|
||||||
|
g_mutex = Kernel::CreateMutex(true, "SRV:Lock");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetProcSemaphore(Service::Interface* self) {
|
void GetProcSemaphore(Service::Interface* self) {
|
||||||
|
DEBUG_LOG(OSHLE, "called");
|
||||||
// Get process semaphore?
|
// Get process semaphore?
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
cmd_buff[3] = 0xDEADBEEF; // Return something... 0 == NULL, raises an exception
|
cmd_buff[1] = 0; // No error
|
||||||
|
cmd_buff[3] = g_mutex; // Return something... 0 == nullptr, raises an exception
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetServiceHandle(Service::Interface* self) {
|
void GetServiceHandle(Service::Interface* self) {
|
||||||
|
@ -29,25 +36,21 @@ void GetServiceHandle(Service::Interface* self) {
|
||||||
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
|
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
|
||||||
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
|
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
|
||||||
|
|
||||||
NOTICE_LOG(OSHLE, "SRV::Sync - GetHandle - port: %s, handle: 0x%08X", port_name.c_str(),
|
if (nullptr != service) {
|
||||||
service->GetHandle());
|
|
||||||
|
|
||||||
if (NULL != service) {
|
|
||||||
cmd_buff[3] = service->GetHandle();
|
cmd_buff[3] = service->GetHandle();
|
||||||
|
DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
|
||||||
} else {
|
} else {
|
||||||
ERROR_LOG(OSHLE, "Service %s does not exist", port_name.c_str());
|
ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
|
||||||
res = -1;
|
res = -1;
|
||||||
}
|
}
|
||||||
cmd_buff[1] = res;
|
cmd_buff[1] = res;
|
||||||
|
|
||||||
//return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Interface::FunctionInfo FunctionTable[] = {
|
const Interface::FunctionInfo FunctionTable[] = {
|
||||||
{0x00010002, Initialize, "Initialize"},
|
{0x00010002, Initialize, "Initialize"},
|
||||||
{0x00020000, GetProcSemaphore, "GetProcSemaphore"},
|
{0x00020000, GetProcSemaphore, "GetProcSemaphore"},
|
||||||
{0x00030100, NULL, "RegisterService"},
|
{0x00030100, nullptr, "RegisterService"},
|
||||||
{0x000400C0, NULL, "UnregisterService"},
|
{0x000400C0, nullptr, "UnregisterService"},
|
||||||
{0x00050100, GetServiceHandle, "GetServiceHandle"},
|
{0x00050100, GetServiceHandle, "GetServiceHandle"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,6 @@ public:
|
||||||
return "srv:";
|
return "srv:";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when svcSendSyncRequest is called, loads command buffer and executes comand
|
|
||||||
* @return Return result of svcSendSyncRequest passed back to user app
|
|
||||||
*/
|
|
||||||
Result Sync();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "core/mem_map.h"
|
#include "core/mem_map.h"
|
||||||
|
|
||||||
|
#include "core/hle/kernel/event.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/mutex.h"
|
#include "core/hle/kernel/mutex.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
@ -16,7 +17,6 @@
|
||||||
#include "core/hle/function_wrappers.h"
|
#include "core/hle/function_wrappers.h"
|
||||||
#include "core/hle/svc.h"
|
#include "core/hle/svc.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Namespace SVC
|
// Namespace SVC
|
||||||
|
@ -34,40 +34,32 @@ enum MapMemoryPermission {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Map application or GSP heap memory
|
/// Map application or GSP heap memory
|
||||||
Result ControlMemory(void* _outaddr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
|
Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
|
||||||
u32* outaddr = (u32*)_outaddr;
|
DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
|
||||||
u32 virtual_address = 0x00000000;
|
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "ControlMemory called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
|
|
||||||
operation, addr0, addr1, size, permissions);
|
operation, addr0, addr1, size, permissions);
|
||||||
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
|
|
||||||
// Map normal heap memory
|
// Map normal heap memory
|
||||||
case MEMORY_OPERATION_HEAP:
|
case MEMORY_OPERATION_HEAP:
|
||||||
virtual_address = Memory::MapBlock_Heap(size, operation, permissions);
|
*out_addr = Memory::MapBlock_Heap(size, operation, permissions);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Map GSP heap memory
|
// Map GSP heap memory
|
||||||
case MEMORY_OPERATION_GSP_HEAP:
|
case MEMORY_OPERATION_GSP_HEAP:
|
||||||
virtual_address = Memory::MapBlock_HeapGSP(size, operation, permissions);
|
*out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Unknown ControlMemory operation
|
// Unknown ControlMemory operation
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(SVC, "ControlMemory unknown operation=0x%08X", operation);
|
ERROR_LOG(SVC, "unknown operation=0x%08X", operation);
|
||||||
}
|
}
|
||||||
if (NULL != outaddr) {
|
|
||||||
*outaddr = virtual_address;
|
|
||||||
}
|
|
||||||
Core::g_app_core->SetReg(1, virtual_address);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a memory block to specified address
|
/// Maps a memory block to specified address
|
||||||
Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherpermission) {
|
Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherpermission) {
|
||||||
DEBUG_LOG(SVC, "MapMemoryBlock called memblock=0x08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
|
DEBUG_LOG(SVC, "called memblock=0x08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
|
||||||
memblock, addr, mypermissions, otherpermission);
|
memblock, addr, mypermissions, otherpermission);
|
||||||
switch (mypermissions) {
|
switch (mypermissions) {
|
||||||
case MEMORY_PERMISSION_NORMAL:
|
case MEMORY_PERMISSION_NORMAL:
|
||||||
|
@ -76,88 +68,146 @@ Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherper
|
||||||
Memory::MapBlock_Shared(memblock, addr, mypermissions);
|
Memory::MapBlock_Shared(memblock, addr, mypermissions);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(OSHLE, "MapMemoryBlock unknown permissions=0x%08X", mypermissions);
|
ERROR_LOG(OSHLE, "unknown permissions=0x%08X", mypermissions);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||||
Result ConnectToPort(void* out, const char* port_name) {
|
Result ConnectToPort(Handle* out, const char* port_name) {
|
||||||
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
|
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
|
||||||
if (service) {
|
|
||||||
Core::g_app_core->SetReg(1, service->GetHandle());
|
DEBUG_LOG(SVC, "called port_name=%s", port_name);
|
||||||
} else {
|
_assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
|
||||||
PanicYesNo("ConnectToPort called port_name=%s, but it is not implemented!", port_name);
|
|
||||||
}
|
*out = service->GetHandle();
|
||||||
DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronize to an OS service
|
/// Synchronize to an OS service
|
||||||
Result SendSyncRequest(Handle handle) {
|
Result SendSyncRequest(Handle handle) {
|
||||||
DEBUG_LOG(SVC, "SendSyncRequest called handle=0x%08X");
|
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
||||||
Service::Interface* service = Service::g_manager->FetchFromHandle(handle);
|
|
||||||
service->Sync();
|
_assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
|
||||||
return 0;
|
DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName());
|
||||||
|
|
||||||
|
bool wait = false;
|
||||||
|
Result res = object->SyncRequest(&wait);
|
||||||
|
if (wait) {
|
||||||
|
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close a handle
|
/// Close a handle
|
||||||
Result CloseHandle(Handle handle) {
|
Result CloseHandle(Handle handle) {
|
||||||
// ImplementMe
|
// ImplementMe
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) CloseHandle called handle=0x%08X", handle);
|
ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
|
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
|
||||||
Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
|
Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d",
|
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
|
||||||
handle, nano_seconds);
|
bool wait = false;
|
||||||
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
|
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
|
||||||
return 0;
|
|
||||||
|
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
||||||
|
|
||||||
|
DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%d", handle, object->GetTypeName(),
|
||||||
|
object->GetName(), nano_seconds);
|
||||||
|
|
||||||
|
_assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
|
||||||
|
|
||||||
|
Result res = object->WaitSynchronization(&wait);
|
||||||
|
|
||||||
|
// Check for next thread to schedule
|
||||||
|
if (wait) {
|
||||||
|
HLE::Reschedule(__func__);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
||||||
Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wait_all, s64 nano_seconds) {
|
Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
|
||||||
s32* out = (s32*)_out;
|
s64 nano_seconds) {
|
||||||
Handle* handles = (Handle*)_handles;
|
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
|
||||||
|
bool unlock_all = true;
|
||||||
|
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronizationN called handle_count=%d, wait_all=%s, nanoseconds=%d %s",
|
DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%d",
|
||||||
handle_count, (wait_all ? "true" : "false"), nano_seconds);
|
handle_count, (wait_all ? "true" : "false"), nano_seconds);
|
||||||
|
|
||||||
for (u32 i = 0; i < handle_count; i++) {
|
// Iterate through each handle, synchronize kernel object
|
||||||
DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]);
|
for (s32 i = 0; i < handle_count; i++) {
|
||||||
|
bool wait = false;
|
||||||
|
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
|
||||||
|
|
||||||
|
_assert_msg_(KERNEL, (object != nullptr), "called handle=0x%08X, but kernel object "
|
||||||
|
"is nullptr!", handles[i]);
|
||||||
|
|
||||||
|
DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName(),
|
||||||
|
object->GetName());
|
||||||
|
|
||||||
|
Result res = object->WaitSynchronization(&wait);
|
||||||
|
|
||||||
|
if (!wait && !wait_all) {
|
||||||
|
*out = i;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
unlock_all = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
|
|
||||||
|
if (wait_all && unlock_all) {
|
||||||
|
*out = handle_count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for next thread to schedule
|
||||||
|
HLE::Reschedule(__func__);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an address arbiter (to allocate access to shared resources)
|
/// Create an address arbiter (to allocate access to shared resources)
|
||||||
Result CreateAddressArbiter(void* arbiter) {
|
Result CreateAddressArbiter(void* arbiter) {
|
||||||
// ImplementMe
|
ERROR_LOG(SVC, "(UNIMPLEMENTED) called");
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateAddressArbiter called");
|
|
||||||
Core::g_app_core->SetReg(1, 0xFABBDADD);
|
Core::g_app_core->SetReg(1, 0xFABBDADD);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Arbitrate address
|
||||||
|
Result ArbitrateAddress(Handle arbiter, u32 addr, u32 _type, u32 value, s64 nanoseconds) {
|
||||||
|
ERROR_LOG(SVC, "(UNIMPLEMENTED) called");
|
||||||
|
ArbitrationType type = (ArbitrationType)_type;
|
||||||
|
Memory::Write32(addr, type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||||
void OutputDebugString(const char* string) {
|
void OutputDebugString(const char* string) {
|
||||||
NOTICE_LOG(SVC, "## OSDEBUG: %08X %s", Core::g_app_core->GetPC(), string);
|
OS_LOG(SVC, "%s", string);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get resource limit
|
/// Get resource limit
|
||||||
Result GetResourceLimit(void* resource_limit, Handle process) {
|
Result GetResourceLimit(Handle* resource_limit, Handle process) {
|
||||||
// With regards to proceess values:
|
// With regards to proceess values:
|
||||||
// 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
|
// 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
|
||||||
// the current KThread.
|
// the current KThread.
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetResourceLimit called process=0x%08X", process);
|
*resource_limit = 0xDEADBEEF;
|
||||||
Core::g_app_core->SetReg(1, 0xDEADBEEF);
|
ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get resource limit current values
|
/// Get resource limit current values
|
||||||
Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* names, s32 name_count) {
|
Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
|
||||||
//s64* values = (s64*)_values;
|
s32 name_count) {
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetResourceLimitCurrentValues called resource_limit=%08X, names=%s, name_count=%d",
|
ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
|
||||||
resource_limit, names, name_count);
|
resource_limit, names, name_count);
|
||||||
Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
|
Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -180,179 +230,234 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p
|
||||||
|
|
||||||
Core::g_app_core->SetReg(1, thread);
|
Core::g_app_core->SetReg(1, thread);
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
|
DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
|
||||||
"threadpriority=0x%08X, processorid=0x%08X : created handle 0x%08X", entry_point,
|
"threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
|
||||||
name.c_str(), arg, stack_top, priority, processor_id, thread);
|
name.c_str(), arg, stack_top, priority, processor_id, thread);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when a thread exits
|
||||||
|
u32 ExitThread() {
|
||||||
|
Handle thread = Kernel::GetCurrentThreadHandle();
|
||||||
|
|
||||||
|
DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
|
||||||
|
|
||||||
|
Kernel::StopThread(thread, __func__);
|
||||||
|
HLE::Reschedule(__func__);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the priority for the specified thread
|
||||||
|
Result GetThreadPriority(s32* priority, Handle handle) {
|
||||||
|
*priority = Kernel::GetThreadPriority(handle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the priority for the specified thread
|
||||||
|
Result SetThreadPriority(Handle handle, s32 priority) {
|
||||||
|
return Kernel::SetThreadPriority(handle, priority);
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a mutex
|
/// Create a mutex
|
||||||
Result CreateMutex(void* _mutex, u32 initial_locked) {
|
Result CreateMutex(Handle* mutex, u32 initial_locked) {
|
||||||
Handle* mutex = (Handle*)_mutex;
|
|
||||||
*mutex = Kernel::CreateMutex((initial_locked != 0));
|
*mutex = Kernel::CreateMutex((initial_locked != 0));
|
||||||
Core::g_app_core->SetReg(1, *mutex);
|
DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X",
|
||||||
DEBUG_LOG(SVC, "CreateMutex called initial_locked=%s : created handle 0x%08X",
|
|
||||||
initial_locked ? "true" : "false", *mutex);
|
initial_locked ? "true" : "false", *mutex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Release a mutex
|
/// Release a mutex
|
||||||
Result ReleaseMutex(Handle handle) {
|
Result ReleaseMutex(Handle handle) {
|
||||||
DEBUG_LOG(SVC, "ReleaseMutex called handle=0x%08X", handle);
|
DEBUG_LOG(SVC, "called handle=0x%08X", handle);
|
||||||
|
_assert_msg_(KERNEL, (handle != 0), "called, but handle is nullptr!");
|
||||||
Kernel::ReleaseMutex(handle);
|
Kernel::ReleaseMutex(handle);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current thread ID
|
/// Get current thread ID
|
||||||
Result GetThreadId(void* thread_id, u32 thread) {
|
Result GetThreadId(u32* thread_id, Handle thread) {
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetThreadId called thread=0x%08X", thread);
|
ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query memory
|
/// Query memory
|
||||||
Result QueryMemory(void *_info, void *_out, u32 addr) {
|
Result QueryMemory(void* info, void* out, u32 addr) {
|
||||||
MemoryInfo* info = (MemoryInfo*) _info;
|
ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
|
||||||
PageInfo* out = (PageInfo*) _out;
|
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) QueryMemory called addr=0x%08X", addr);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an event
|
/// Create an event
|
||||||
Result CreateEvent(void* _event, u32 reset_type) {
|
Result CreateEvent(Handle* evt, u32 reset_type) {
|
||||||
Handle* event = (Handle*)_event;
|
*evt = Kernel::CreateEvent((ResetType)reset_type);
|
||||||
DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateEvent called reset_type=0x%08X", reset_type);
|
DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X",
|
||||||
Core::g_app_core->SetReg(1, 0xBADC0DE0);
|
reset_type, *evt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Duplicates a kernel handle
|
||||||
|
Result DuplicateHandle(Handle* out, Handle handle) {
|
||||||
|
DEBUG_LOG(SVC, "called handle=0x%08X", handle);
|
||||||
|
|
||||||
|
// Translate kernel handles -> real handles
|
||||||
|
if (handle == Kernel::CurrentThread) {
|
||||||
|
handle = Kernel::GetCurrentThreadHandle();
|
||||||
|
}
|
||||||
|
_assert_msg_(KERNEL, (handle != Kernel::CurrentProcess),
|
||||||
|
"(UNIMPLEMENTED) process handle duplication!");
|
||||||
|
|
||||||
|
// TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate.
|
||||||
|
*out = handle;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signals an event
|
||||||
|
Result SignalEvent(Handle evt) {
|
||||||
|
Result res = Kernel::SignalEvent(evt);
|
||||||
|
DEBUG_LOG(SVC, "called event=0x%08X", evt);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears an event
|
||||||
|
Result ClearEvent(Handle evt) {
|
||||||
|
Result res = Kernel::ClearEvent(evt);
|
||||||
|
DEBUG_LOG(SVC, "called event=0x%08X", evt);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sleep the current thread
|
||||||
|
void SleepThread(s64 nanoseconds) {
|
||||||
|
DEBUG_LOG(SVC, "called nanoseconds=%d", nanoseconds);
|
||||||
|
}
|
||||||
|
|
||||||
const HLE::FunctionDef SVC_Table[] = {
|
const HLE::FunctionDef SVC_Table[] = {
|
||||||
{0x00, NULL, "Unknown"},
|
{0x00, nullptr, "Unknown"},
|
||||||
{0x01, WrapI_VUUUUU<ControlMemory>, "ControlMemory"},
|
{0x01, HLE::Wrap<ControlMemory>, "ControlMemory"},
|
||||||
{0x02, WrapI_VVU<QueryMemory>, "QueryMemory"},
|
{0x02, HLE::Wrap<QueryMemory>, "QueryMemory"},
|
||||||
{0x03, NULL, "ExitProcess"},
|
{0x03, nullptr, "ExitProcess"},
|
||||||
{0x04, NULL, "GetProcessAffinityMask"},
|
{0x04, nullptr, "GetProcessAffinityMask"},
|
||||||
{0x05, NULL, "SetProcessAffinityMask"},
|
{0x05, nullptr, "SetProcessAffinityMask"},
|
||||||
{0x06, NULL, "GetProcessIdealProcessor"},
|
{0x06, nullptr, "GetProcessIdealProcessor"},
|
||||||
{0x07, NULL, "SetProcessIdealProcessor"},
|
{0x07, nullptr, "SetProcessIdealProcessor"},
|
||||||
{0x08, WrapI_UUUUU<CreateThread>, "CreateThread"},
|
{0x08, HLE::Wrap<CreateThread>, "CreateThread"},
|
||||||
{0x09, NULL, "ExitThread"},
|
{0x09, HLE::Wrap<ExitThread>, "ExitThread"},
|
||||||
{0x0A, NULL, "SleepThread"},
|
{0x0A, HLE::Wrap<SleepThread>, "SleepThread"},
|
||||||
{0x0B, NULL, "GetThreadPriority"},
|
{0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"},
|
||||||
{0x0C, NULL, "SetThreadPriority"},
|
{0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"},
|
||||||
{0x0D, NULL, "GetThreadAffinityMask"},
|
{0x0D, nullptr, "GetThreadAffinityMask"},
|
||||||
{0x0E, NULL, "SetThreadAffinityMask"},
|
{0x0E, nullptr, "SetThreadAffinityMask"},
|
||||||
{0x0F, NULL, "GetThreadIdealProcessor"},
|
{0x0F, nullptr, "GetThreadIdealProcessor"},
|
||||||
{0x10, NULL, "SetThreadIdealProcessor"},
|
{0x10, nullptr, "SetThreadIdealProcessor"},
|
||||||
{0x11, NULL, "GetCurrentProcessorNumber"},
|
{0x11, nullptr, "GetCurrentProcessorNumber"},
|
||||||
{0x12, NULL, "Run"},
|
{0x12, nullptr, "Run"},
|
||||||
{0x13, WrapI_VU<CreateMutex>, "CreateMutex"},
|
{0x13, HLE::Wrap<CreateMutex>, "CreateMutex"},
|
||||||
{0x14, WrapI_U<ReleaseMutex>, "ReleaseMutex"},
|
{0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"},
|
||||||
{0x15, NULL, "CreateSemaphore"},
|
{0x15, nullptr, "CreateSemaphore"},
|
||||||
{0x16, NULL, "ReleaseSemaphore"},
|
{0x16, nullptr, "ReleaseSemaphore"},
|
||||||
{0x17, WrapI_VU<CreateEvent>, "CreateEvent"},
|
{0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
|
||||||
{0x18, NULL, "SignalEvent"},
|
{0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
|
||||||
{0x19, NULL, "ClearEvent"},
|
{0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
|
||||||
{0x1A, NULL, "CreateTimer"},
|
{0x1A, nullptr, "CreateTimer"},
|
||||||
{0x1B, NULL, "SetTimer"},
|
{0x1B, nullptr, "SetTimer"},
|
||||||
{0x1C, NULL, "CancelTimer"},
|
{0x1C, nullptr, "CancelTimer"},
|
||||||
{0x1D, NULL, "ClearTimer"},
|
{0x1D, nullptr, "ClearTimer"},
|
||||||
{0x1E, NULL, "CreateMemoryBlock"},
|
{0x1E, nullptr, "CreateMemoryBlock"},
|
||||||
{0x1F, WrapI_UUUU<MapMemoryBlock>, "MapMemoryBlock"},
|
{0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
|
||||||
{0x20, NULL, "UnmapMemoryBlock"},
|
{0x20, nullptr, "UnmapMemoryBlock"},
|
||||||
{0x21, WrapI_V<CreateAddressArbiter>, "CreateAddressArbiter"},
|
{0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"},
|
||||||
{0x22, NULL, "ArbitrateAddress"},
|
{0x22, HLE::Wrap<ArbitrateAddress>, "ArbitrateAddress"},
|
||||||
{0x23, WrapI_U<CloseHandle>, "CloseHandle"},
|
{0x23, HLE::Wrap<CloseHandle>, "CloseHandle"},
|
||||||
{0x24, WrapI_US64<WaitSynchronization1>, "WaitSynchronization1"},
|
{0x24, HLE::Wrap<WaitSynchronization1>, "WaitSynchronization1"},
|
||||||
{0x25, WrapI_VVUUS64<WaitSynchronizationN>, "WaitSynchronizationN"},
|
{0x25, HLE::Wrap<WaitSynchronizationN>, "WaitSynchronizationN"},
|
||||||
{0x26, NULL, "SignalAndWait"},
|
{0x26, nullptr, "SignalAndWait"},
|
||||||
{0x27, NULL, "DuplicateHandle"},
|
{0x27, HLE::Wrap<DuplicateHandle>, "DuplicateHandle"},
|
||||||
{0x28, NULL, "GetSystemTick"},
|
{0x28, nullptr, "GetSystemTick"},
|
||||||
{0x29, NULL, "GetHandleInfo"},
|
{0x29, nullptr, "GetHandleInfo"},
|
||||||
{0x2A, NULL, "GetSystemInfo"},
|
{0x2A, nullptr, "GetSystemInfo"},
|
||||||
{0x2B, NULL, "GetProcessInfo"},
|
{0x2B, nullptr, "GetProcessInfo"},
|
||||||
{0x2C, NULL, "GetThreadInfo"},
|
{0x2C, nullptr, "GetThreadInfo"},
|
||||||
{0x2D, WrapI_VC<ConnectToPort>, "ConnectToPort"},
|
{0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"},
|
||||||
{0x2E, NULL, "SendSyncRequest1"},
|
{0x2E, nullptr, "SendSyncRequest1"},
|
||||||
{0x2F, NULL, "SendSyncRequest2"},
|
{0x2F, nullptr, "SendSyncRequest2"},
|
||||||
{0x30, NULL, "SendSyncRequest3"},
|
{0x30, nullptr, "SendSyncRequest3"},
|
||||||
{0x31, NULL, "SendSyncRequest4"},
|
{0x31, nullptr, "SendSyncRequest4"},
|
||||||
{0x32, WrapI_U<SendSyncRequest>, "SendSyncRequest"},
|
{0x32, HLE::Wrap<SendSyncRequest>, "SendSyncRequest"},
|
||||||
{0x33, NULL, "OpenProcess"},
|
{0x33, nullptr, "OpenProcess"},
|
||||||
{0x34, NULL, "OpenThread"},
|
{0x34, nullptr, "OpenThread"},
|
||||||
{0x35, NULL, "GetProcessId"},
|
{0x35, nullptr, "GetProcessId"},
|
||||||
{0x36, NULL, "GetProcessIdOfThread"},
|
{0x36, nullptr, "GetProcessIdOfThread"},
|
||||||
{0x37, WrapI_VU<GetThreadId>, "GetThreadId"},
|
{0x37, HLE::Wrap<GetThreadId>, "GetThreadId"},
|
||||||
{0x38, WrapI_VU<GetResourceLimit>, "GetResourceLimit"},
|
{0x38, HLE::Wrap<GetResourceLimit>, "GetResourceLimit"},
|
||||||
{0x39, NULL, "GetResourceLimitLimitValues"},
|
{0x39, nullptr, "GetResourceLimitLimitValues"},
|
||||||
{0x3A, WrapI_VUVI<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"},
|
{0x3A, HLE::Wrap<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"},
|
||||||
{0x3B, NULL, "GetThreadContext"},
|
{0x3B, nullptr, "GetThreadContext"},
|
||||||
{0x3C, NULL, "Break"},
|
{0x3C, nullptr, "Break"},
|
||||||
{0x3D, WrapV_C<OutputDebugString>, "OutputDebugString"},
|
{0x3D, HLE::Wrap<OutputDebugString>, "OutputDebugString"},
|
||||||
{0x3E, NULL, "ControlPerformanceCounter"},
|
{0x3E, nullptr, "ControlPerformanceCounter"},
|
||||||
{0x3F, NULL, "Unknown"},
|
{0x3F, nullptr, "Unknown"},
|
||||||
{0x40, NULL, "Unknown"},
|
{0x40, nullptr, "Unknown"},
|
||||||
{0x41, NULL, "Unknown"},
|
{0x41, nullptr, "Unknown"},
|
||||||
{0x42, NULL, "Unknown"},
|
{0x42, nullptr, "Unknown"},
|
||||||
{0x43, NULL, "Unknown"},
|
{0x43, nullptr, "Unknown"},
|
||||||
{0x44, NULL, "Unknown"},
|
{0x44, nullptr, "Unknown"},
|
||||||
{0x45, NULL, "Unknown"},
|
{0x45, nullptr, "Unknown"},
|
||||||
{0x46, NULL, "Unknown"},
|
{0x46, nullptr, "Unknown"},
|
||||||
{0x47, NULL, "CreatePort"},
|
{0x47, nullptr, "CreatePort"},
|
||||||
{0x48, NULL, "CreateSessionToPort"},
|
{0x48, nullptr, "CreateSessionToPort"},
|
||||||
{0x49, NULL, "CreateSession"},
|
{0x49, nullptr, "CreateSession"},
|
||||||
{0x4A, NULL, "AcceptSession"},
|
{0x4A, nullptr, "AcceptSession"},
|
||||||
{0x4B, NULL, "ReplyAndReceive1"},
|
{0x4B, nullptr, "ReplyAndReceive1"},
|
||||||
{0x4C, NULL, "ReplyAndReceive2"},
|
{0x4C, nullptr, "ReplyAndReceive2"},
|
||||||
{0x4D, NULL, "ReplyAndReceive3"},
|
{0x4D, nullptr, "ReplyAndReceive3"},
|
||||||
{0x4E, NULL, "ReplyAndReceive4"},
|
{0x4E, nullptr, "ReplyAndReceive4"},
|
||||||
{0x4F, NULL, "ReplyAndReceive"},
|
{0x4F, nullptr, "ReplyAndReceive"},
|
||||||
{0x50, NULL, "BindInterrupt"},
|
{0x50, nullptr, "BindInterrupt"},
|
||||||
{0x51, NULL, "UnbindInterrupt"},
|
{0x51, nullptr, "UnbindInterrupt"},
|
||||||
{0x52, NULL, "InvalidateProcessDataCache"},
|
{0x52, nullptr, "InvalidateProcessDataCache"},
|
||||||
{0x53, NULL, "StoreProcessDataCache"},
|
{0x53, nullptr, "StoreProcessDataCache"},
|
||||||
{0x54, NULL, "FlushProcessDataCache"},
|
{0x54, nullptr, "FlushProcessDataCache"},
|
||||||
{0x55, NULL, "StartInterProcessDma"},
|
{0x55, nullptr, "StartInterProcessDma"},
|
||||||
{0x56, NULL, "StopDma"},
|
{0x56, nullptr, "StopDma"},
|
||||||
{0x57, NULL, "GetDmaState"},
|
{0x57, nullptr, "GetDmaState"},
|
||||||
{0x58, NULL, "RestartDma"},
|
{0x58, nullptr, "RestartDma"},
|
||||||
{0x59, NULL, "Unknown"},
|
{0x59, nullptr, "Unknown"},
|
||||||
{0x5A, NULL, "Unknown"},
|
{0x5A, nullptr, "Unknown"},
|
||||||
{0x5B, NULL, "Unknown"},
|
{0x5B, nullptr, "Unknown"},
|
||||||
{0x5C, NULL, "Unknown"},
|
{0x5C, nullptr, "Unknown"},
|
||||||
{0x5D, NULL, "Unknown"},
|
{0x5D, nullptr, "Unknown"},
|
||||||
{0x5E, NULL, "Unknown"},
|
{0x5E, nullptr, "Unknown"},
|
||||||
{0x5F, NULL, "Unknown"},
|
{0x5F, nullptr, "Unknown"},
|
||||||
{0x60, NULL, "DebugActiveProcess"},
|
{0x60, nullptr, "DebugActiveProcess"},
|
||||||
{0x61, NULL, "BreakDebugProcess"},
|
{0x61, nullptr, "BreakDebugProcess"},
|
||||||
{0x62, NULL, "TerminateDebugProcess"},
|
{0x62, nullptr, "TerminateDebugProcess"},
|
||||||
{0x63, NULL, "GetProcessDebugEvent"},
|
{0x63, nullptr, "GetProcessDebugEvent"},
|
||||||
{0x64, NULL, "ContinueDebugEvent"},
|
{0x64, nullptr, "ContinueDebugEvent"},
|
||||||
{0x65, NULL, "GetProcessList"},
|
{0x65, nullptr, "GetProcessList"},
|
||||||
{0x66, NULL, "GetThreadList"},
|
{0x66, nullptr, "GetThreadList"},
|
||||||
{0x67, NULL, "GetDebugThreadContext"},
|
{0x67, nullptr, "GetDebugThreadContext"},
|
||||||
{0x68, NULL, "SetDebugThreadContext"},
|
{0x68, nullptr, "SetDebugThreadContext"},
|
||||||
{0x69, NULL, "QueryDebugProcessMemory"},
|
{0x69, nullptr, "QueryDebugProcessMemory"},
|
||||||
{0x6A, NULL, "ReadProcessMemory"},
|
{0x6A, nullptr, "ReadProcessMemory"},
|
||||||
{0x6B, NULL, "WriteProcessMemory"},
|
{0x6B, nullptr, "WriteProcessMemory"},
|
||||||
{0x6C, NULL, "SetHardwareBreakPoint"},
|
{0x6C, nullptr, "SetHardwareBreakPoint"},
|
||||||
{0x6D, NULL, "GetDebugThreadParam"},
|
{0x6D, nullptr, "GetDebugThreadParam"},
|
||||||
{0x6E, NULL, "Unknown"},
|
{0x6E, nullptr, "Unknown"},
|
||||||
{0x6F, NULL, "Unknown"},
|
{0x6F, nullptr, "Unknown"},
|
||||||
{0x70, NULL, "ControlProcessMemory"},
|
{0x70, nullptr, "ControlProcessMemory"},
|
||||||
{0x71, NULL, "MapProcessMemory"},
|
{0x71, nullptr, "MapProcessMemory"},
|
||||||
{0x72, NULL, "UnmapProcessMemory"},
|
{0x72, nullptr, "UnmapProcessMemory"},
|
||||||
{0x73, NULL, "Unknown"},
|
{0x73, nullptr, "Unknown"},
|
||||||
{0x74, NULL, "Unknown"},
|
{0x74, nullptr, "Unknown"},
|
||||||
{0x75, NULL, "Unknown"},
|
{0x75, nullptr, "Unknown"},
|
||||||
{0x76, NULL, "TerminateProcess"},
|
{0x76, nullptr, "TerminateProcess"},
|
||||||
{0x77, NULL, "Unknown"},
|
{0x77, nullptr, "Unknown"},
|
||||||
{0x78, NULL, "CreateResourceLimit"},
|
{0x78, nullptr, "CreateResourceLimit"},
|
||||||
{0x79, NULL, "Unknown"},
|
{0x79, nullptr, "Unknown"},
|
||||||
{0x7A, NULL, "Unknown"},
|
{0x7A, nullptr, "Unknown"},
|
||||||
{0x7B, NULL, "Unknown"},
|
{0x7B, nullptr, "Unknown"},
|
||||||
{0x7C, NULL, "KernelSetState"},
|
{0x7C, nullptr, "KernelSetState"},
|
||||||
{0x7D, NULL, "QueryProcessMemory"},
|
{0x7D, nullptr, "QueryProcessMemory"},
|
||||||
};
|
};
|
||||||
|
|
||||||
void Register() {
|
void Register() {
|
||||||
|
|
|
@ -29,6 +29,10 @@ struct ThreadContext {
|
||||||
u32 fpu_registers[32];
|
u32 fpu_registers[32];
|
||||||
u32 fpscr;
|
u32 fpscr;
|
||||||
u32 fpexc;
|
u32 fpexc;
|
||||||
|
|
||||||
|
// These are not part of native ThreadContext, but needed by emu
|
||||||
|
u32 reg_15;
|
||||||
|
u32 mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ResetType {
|
enum ResetType {
|
||||||
|
@ -38,6 +42,15 @@ enum ResetType {
|
||||||
RESETTYPE_MAX_BIT = (1u << 31),
|
RESETTYPE_MAX_BIT = (1u << 31),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ArbitrationType {
|
||||||
|
ARBITRATIONTYPE_SIGNAL,
|
||||||
|
ARBITRATIONTYPE_WAIT_IF_LESS_THAN,
|
||||||
|
ARBITRATIONTYPE_DECREMENT_AND_WAIT_IF_LESS_THAN,
|
||||||
|
ARBITRATIONTYPE_WAIT_IF_LESS_THAN_WITH_TIMEOUT,
|
||||||
|
ARBITRATIONTYPE_DECREMENT_AND_WAIT_IF_LESS_THAN_WITH_TIMEOUT,
|
||||||
|
ARBITRATIONTYPE_MAX_BIT = (1u << 31)
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Namespace SVC
|
// Namespace SVC
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,6 @@ namespace GPU {
|
||||||
|
|
||||||
Registers g_regs;
|
Registers g_regs;
|
||||||
|
|
||||||
static const u32 kFrameTicks = 268123480 / 60; ///< 268MHz / 60 frames per second
|
|
||||||
|
|
||||||
u64 g_last_ticks = 0; ///< Last CPU ticks
|
u64 g_last_ticks = 0; ///< Last CPU ticks
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
|
|
||||||
namespace GPU {
|
namespace GPU {
|
||||||
|
|
||||||
|
static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second
|
||||||
|
static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame
|
||||||
|
|
||||||
struct Registers {
|
struct Registers {
|
||||||
enum Id : u32 {
|
enum Id : u32 {
|
||||||
FramebufferTopLeft1 = 0x1EF00468, // Main LCD, first framebuffer for 3D left
|
FramebufferTopLeft1 = 0x1EF00468, // Main LCD, first framebuffer for 3D left
|
||||||
|
|
|
@ -86,7 +86,7 @@ inline void _Read(T &var, const u32 addr) {
|
||||||
var = *((const T*)&g_vram[vaddr & VRAM_MASK]);
|
var = *((const T*)&g_vram[vaddr & VRAM_MASK]);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//_assert_msg_(MEMMAP, false, "unknown Read%d @ 0x%08X", sizeof(var) * 8, vaddr);
|
ERROR_LOG(MEMMAP, "unknown Read%d @ 0x%08X", sizeof(var) * 8, vaddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,8 +136,7 @@ inline void _Write(u32 addr, const T data) {
|
||||||
|
|
||||||
// Error out...
|
// Error out...
|
||||||
} else {
|
} else {
|
||||||
_assert_msg_(MEMMAP, false, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8,
|
ERROR_LOG(MEMMAP, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, vaddr);
|
||||||
data, vaddr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
#include "core/hw/hw.h"
|
#include "core/hw/hw.h"
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ void Init(EmuWindow* emu_window) {
|
||||||
HLE::Init();
|
HLE::Init();
|
||||||
CoreTiming::Init();
|
CoreTiming::Init();
|
||||||
VideoCore::Init(emu_window);
|
VideoCore::Init(emu_window);
|
||||||
|
Kernel::Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunLoopFor(int cycles) {
|
void RunLoopFor(int cycles) {
|
||||||
|
@ -42,6 +44,7 @@ void Shutdown() {
|
||||||
HLE::Shutdown();
|
HLE::Shutdown();
|
||||||
CoreTiming::Shutdown();
|
CoreTiming::Shutdown();
|
||||||
VideoCore::Shutdown();
|
VideoCore::Shutdown();
|
||||||
|
Kernel::Shutdown();
|
||||||
g_ctr_file_system.Shutdown();
|
g_ctr_file_system.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue