2019-08-17 13:51:31 -04:00
|
|
|
#include <cstring>
|
2015-05-06 00:54:15 -04:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <exception>
|
2017-02-26 23:14:17 +00:00
|
|
|
#include "string_format.h"
|
2015-05-06 00:54:15 -04:00
|
|
|
#include "PS2OS.h"
|
|
|
|
#include "StdStream.h"
|
2016-05-23 13:30:46 -04:00
|
|
|
#ifdef __ANDROID__
|
|
|
|
#include "android/AssetStream.h"
|
2023-04-26 13:33:32 -04:00
|
|
|
#include "android/ContentStream.h"
|
|
|
|
#include "android/ContentUtils.h"
|
2016-05-23 13:30:46 -04:00
|
|
|
#endif
|
2015-05-06 00:54:15 -04:00
|
|
|
#include "../Ps2Const.h"
|
2015-07-22 00:30:06 -04:00
|
|
|
#include "../DiskUtils.h"
|
2015-05-06 00:54:15 -04:00
|
|
|
#include "../ElfFile.h"
|
|
|
|
#include "../COP_SCU.h"
|
|
|
|
#include "../uint128.h"
|
2025-03-11 12:48:26 -04:00
|
|
|
#include "Log.h"
|
2015-05-06 00:54:15 -04:00
|
|
|
#include "../iop/IopBios.h"
|
|
|
|
#include "DMAC.h"
|
|
|
|
#include "INTC.h"
|
2019-01-02 21:34:29 -05:00
|
|
|
#include "Timer.h"
|
2015-05-06 00:54:15 -04:00
|
|
|
#include "SIF.h"
|
|
|
|
#include "EEAssembler.h"
|
2025-01-13 18:15:07 -05:00
|
|
|
#include "EeExecutor.h"
|
2015-05-06 00:54:15 -04:00
|
|
|
#include "PathUtils.h"
|
|
|
|
#include "xml/Node.h"
|
|
|
|
#include "xml/Parser.h"
|
|
|
|
#include "xml/FilteringNodeIterator.h"
|
|
|
|
#include "StdStreamUtils.h"
|
2020-09-09 17:13:17 -04:00
|
|
|
#include "AppConfig.h"
|
|
|
|
#include "PS2VM_Preferences.h"
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2025-04-07 10:35:12 -04:00
|
|
|
#define BIOS_ADDRESS_STATE_BASE 0x4000
|
2023-11-16 17:58:37 -05:00
|
|
|
#define BIOS_ADDRESS_STATE_ITEM(a) (BIOS_ADDRESS_STATE_BASE + offsetof(BIOS_STATE, a))
|
2023-11-10 16:52:50 -05:00
|
|
|
|
|
|
|
#define BIOS_ADDRESS_INTERRUPT_THREAD_CONTEXT (BIOS_ADDRESS_STATE_BASE + sizeof(BIOS_STATE))
|
2024-06-04 17:20:45 -04:00
|
|
|
#define BIOS_ADDRESS_DECI2HANDLER_BASE 0x00008000
|
2018-04-30 21:01:23 +01:00
|
|
|
#define BIOS_ADDRESS_INTCHANDLER_BASE 0x0000A000
|
|
|
|
#define BIOS_ADDRESS_DMACHANDLER_BASE 0x0000C000
|
|
|
|
#define BIOS_ADDRESS_SEMAPHORE_BASE 0x0000E000
|
2019-05-23 21:13:37 -04:00
|
|
|
#define BIOS_ADDRESS_CUSTOMSYSCALL_BASE 0x00010000
|
2018-04-30 21:01:23 +01:00
|
|
|
#define BIOS_ADDRESS_ALARM_BASE 0x00010800
|
|
|
|
#define BIOS_ADDRESS_THREAD_BASE 0x00011000
|
2023-06-05 15:39:18 -04:00
|
|
|
#define BIOS_ADDRESS_KERNELSTACK_TOP 0x00030000
|
2018-04-30 21:01:23 +01:00
|
|
|
|
|
|
|
#define BIOS_ADDRESS_BASE 0x1FC00000
|
|
|
|
#define BIOS_ADDRESS_INTERRUPTHANDLER 0x1FC00200
|
|
|
|
#define BIOS_ADDRESS_DMACHANDLER 0x1FC01000
|
|
|
|
#define BIOS_ADDRESS_INTCHANDLER 0x1FC02000
|
|
|
|
#define BIOS_ADDRESS_THREADEPILOG 0x1FC03000
|
|
|
|
#define BIOS_ADDRESS_IDLETHREADPROC 0x1FC03100
|
|
|
|
#define BIOS_ADDRESS_ALARMHANDLER 0x1FC03200
|
|
|
|
|
|
|
|
#define BIOS_ID_BASE 1
|
|
|
|
|
2021-04-22 16:23:27 -04:00
|
|
|
//Some notes about SEMA_ID_BASE:
|
|
|
|
// 2K games (ex.: NBA 2K12) seem to have a bug where they wait for a semaphore they don't own.
|
|
|
|
// The semaphore id is never set and the game will use the id in WaitSema and SignalSema.
|
2021-04-22 17:16:12 -04:00
|
|
|
// On a real PS2, this seem to work because ids start at 0. The first semaphore created in the
|
2021-04-22 16:23:27 -04:00
|
|
|
// game's lifetime doesn't seem to be used, so this bug probably doesn't have any side effect.
|
|
|
|
#define SEMA_ID_BASE 0
|
|
|
|
|
2025-01-13 18:15:07 -05:00
|
|
|
#define GAMECONFIG_FILENAME "GameConfig.xml"
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
#define LOG_NAME ("ps2os")
|
|
|
|
|
2019-02-09 11:16:59 -05:00
|
|
|
#define SYSCALL_CUSTOM_RESCHEDULE 0x666
|
2019-02-09 11:26:48 -05:00
|
|
|
#define SYSCALL_CUSTOM_EXITINTERRUPT 0x667
|
2019-02-09 11:16:59 -05:00
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
#define SYSCALL_NAME_EXIT "osExit"
|
|
|
|
#define SYSCALL_NAME_LOADEXECPS2 "osLoadExecPS2"
|
|
|
|
#define SYSCALL_NAME_EXECPS2 "osExecPS2"
|
2018-08-27 07:31:18 -04:00
|
|
|
#define SYSCALL_NAME_SETVTLBREFILLHANDLER "osSetVTLBRefillHandler"
|
|
|
|
#define SYSCALL_NAME_SETVCOMMONHANDLER "osSetVCommonHandler"
|
2018-04-30 21:01:23 +01:00
|
|
|
#define SYSCALL_NAME_ADDINTCHANDLER "osAddIntcHandler"
|
|
|
|
#define SYSCALL_NAME_REMOVEINTCHANDLER "osRemoveIntcHandler"
|
|
|
|
#define SYSCALL_NAME_ADDDMACHANDLER "osAddDmacHandler"
|
|
|
|
#define SYSCALL_NAME_REMOVEDMACHANDLER "osRemoveDmacHandler"
|
|
|
|
#define SYSCALL_NAME_ENABLEINTC "osEnableIntc"
|
|
|
|
#define SYSCALL_NAME_DISABLEINTC "osDisableIntc"
|
|
|
|
#define SYSCALL_NAME_ENABLEDMAC "osEnableDmac"
|
|
|
|
#define SYSCALL_NAME_DISABLEDMAC "osDisableDmac"
|
|
|
|
#define SYSCALL_NAME_SETALARM "osSetAlarm"
|
|
|
|
#define SYSCALL_NAME_IENABLEINTC "osiEnableIntc"
|
|
|
|
#define SYSCALL_NAME_IDISABLEINTC "osiDisableIntc"
|
|
|
|
#define SYSCALL_NAME_IENABLEDMAC "osiEnableDmac"
|
|
|
|
#define SYSCALL_NAME_IDISABLEDMAC "osiDisableDmac"
|
2019-08-16 07:49:13 -04:00
|
|
|
#define SYSCALL_NAME_ISETALARM "osiSetAlarm"
|
2018-04-30 21:01:23 +01:00
|
|
|
#define SYSCALL_NAME_IRELEASEALARM "osiReleaseAlarm"
|
|
|
|
#define SYSCALL_NAME_CREATETHREAD "osCreateThread"
|
|
|
|
#define SYSCALL_NAME_DELETETHREAD "osDeleteThread"
|
|
|
|
#define SYSCALL_NAME_STARTTHREAD "osStartThread"
|
|
|
|
#define SYSCALL_NAME_EXITTHREAD "osExitThread"
|
|
|
|
#define SYSCALL_NAME_EXITDELETETHREAD "osExitDeleteThread"
|
|
|
|
#define SYSCALL_NAME_TERMINATETHREAD "osTerminateThread"
|
|
|
|
#define SYSCALL_NAME_CHANGETHREADPRIORITY "osChangeThreadPriority"
|
|
|
|
#define SYSCALL_NAME_ICHANGETHREADPRIORITY "osiChangeThreadPriority"
|
|
|
|
#define SYSCALL_NAME_ROTATETHREADREADYQUEUE "osRotateThreadReadyQueue"
|
2018-06-19 19:12:34 -04:00
|
|
|
#define SYSCALL_NAME_RELEASEWAITTHREAD "osReleaseWaitThread"
|
|
|
|
#define SYSCALL_NAME_IRELEASEWAITTHREAD "osiReleaseWaitThread"
|
2018-04-30 21:01:23 +01:00
|
|
|
#define SYSCALL_NAME_GETTHREADID "osGetThreadId"
|
|
|
|
#define SYSCALL_NAME_REFERTHREADSTATUS "osReferThreadStatus"
|
|
|
|
#define SYSCALL_NAME_IREFERTHREADSTATUS "osiReferThreadStatus"
|
2018-05-15 12:44:16 -04:00
|
|
|
#define SYSCALL_NAME_GETOSDCONFIGPARAM "osGetOsdConfigParam"
|
2023-09-21 09:05:03 -04:00
|
|
|
#define SYSCALL_NAME_GETCOP0 "osGetCop0"
|
2018-04-30 21:01:23 +01:00
|
|
|
#define SYSCALL_NAME_SLEEPTHREAD "osSleepThread"
|
|
|
|
#define SYSCALL_NAME_WAKEUPTHREAD "osWakeupThread"
|
|
|
|
#define SYSCALL_NAME_IWAKEUPTHREAD "osiWakeupThread"
|
|
|
|
#define SYSCALL_NAME_CANCELWAKEUPTHREAD "osCancelWakeupThread"
|
|
|
|
#define SYSCALL_NAME_ICANCELWAKEUPTHREAD "osiCancelWakeupThread"
|
|
|
|
#define SYSCALL_NAME_SUSPENDTHREAD "osSuspendThread"
|
|
|
|
#define SYSCALL_NAME_ISUSPENDTHREAD "osiSuspendThread"
|
|
|
|
#define SYSCALL_NAME_RESUMETHREAD "osResumeThread"
|
2023-08-18 12:40:31 -04:00
|
|
|
#define SYSCALL_NAME_IRESUMETHREAD "osiResumeThread"
|
2018-04-30 21:01:23 +01:00
|
|
|
#define SYSCALL_NAME_ENDOFHEAP "osEndOfHeap"
|
|
|
|
#define SYSCALL_NAME_CREATESEMA "osCreateSema"
|
|
|
|
#define SYSCALL_NAME_DELETESEMA "osDeleteSema"
|
|
|
|
#define SYSCALL_NAME_SIGNALSEMA "osSignalSema"
|
|
|
|
#define SYSCALL_NAME_ISIGNALSEMA "osiSignalSema"
|
|
|
|
#define SYSCALL_NAME_WAITSEMA "osWaitSema"
|
|
|
|
#define SYSCALL_NAME_POLLSEMA "osPollSema"
|
|
|
|
#define SYSCALL_NAME_IPOLLSEMA "osiPollSema"
|
|
|
|
#define SYSCALL_NAME_REFERSEMASTATUS "osReferSemaStatus"
|
|
|
|
#define SYSCALL_NAME_IREFERSEMASTATUS "osiReferSemaStatus"
|
|
|
|
#define SYSCALL_NAME_FLUSHCACHE "osFlushCache"
|
|
|
|
#define SYSCALL_NAME_SIFSTOPDMA "osSifStopDma"
|
|
|
|
#define SYSCALL_NAME_GSGETIMR "osGsGetIMR"
|
|
|
|
#define SYSCALL_NAME_GSPUTIMR "osGsPutIMR"
|
|
|
|
#define SYSCALL_NAME_SETVSYNCFLAG "osSetVSyncFlag"
|
|
|
|
#define SYSCALL_NAME_SETSYSCALL "osSetSyscall"
|
|
|
|
#define SYSCALL_NAME_SIFDMASTAT "osSifDmaStat"
|
|
|
|
#define SYSCALL_NAME_SIFSETDMA "osSifSetDma"
|
|
|
|
#define SYSCALL_NAME_SIFSETDCHAIN "osSifSetDChain"
|
|
|
|
#define SYSCALL_NAME_DECI2CALL "osDeci2Call"
|
|
|
|
#define SYSCALL_NAME_MACHINETYPE "osMachineType"
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
|
2023-08-18 12:34:46 -04:00
|
|
|
// clang-format off
|
2018-04-30 21:01:23 +01:00
|
|
|
const CPS2OS::SYSCALL_NAME CPS2OS::g_syscallNames[] =
|
2023-08-18 12:34:46 -04:00
|
|
|
{
|
|
|
|
{0x0004, SYSCALL_NAME_EXIT},
|
|
|
|
{0x0006, SYSCALL_NAME_LOADEXECPS2},
|
|
|
|
{0x0007, SYSCALL_NAME_EXECPS2},
|
|
|
|
{0x000D, SYSCALL_NAME_SETVTLBREFILLHANDLER},
|
|
|
|
{0x000E, SYSCALL_NAME_SETVCOMMONHANDLER},
|
|
|
|
{0x0010, SYSCALL_NAME_ADDINTCHANDLER},
|
|
|
|
{0x0011, SYSCALL_NAME_REMOVEINTCHANDLER},
|
|
|
|
{0x0012, SYSCALL_NAME_ADDDMACHANDLER},
|
|
|
|
{0x0013, SYSCALL_NAME_REMOVEDMACHANDLER},
|
|
|
|
{0x0014, SYSCALL_NAME_ENABLEINTC},
|
|
|
|
{0x0015, SYSCALL_NAME_DISABLEINTC},
|
|
|
|
{0x0016, SYSCALL_NAME_ENABLEDMAC},
|
|
|
|
{0x0017, SYSCALL_NAME_DISABLEDMAC},
|
|
|
|
{0x0018, SYSCALL_NAME_SETALARM},
|
|
|
|
{0x001A, SYSCALL_NAME_IENABLEINTC},
|
|
|
|
{0x001B, SYSCALL_NAME_IDISABLEINTC},
|
|
|
|
{0x001C, SYSCALL_NAME_IENABLEDMAC},
|
|
|
|
{0x001D, SYSCALL_NAME_IDISABLEDMAC},
|
|
|
|
{0x001E, SYSCALL_NAME_ISETALARM},
|
|
|
|
{0x001F, SYSCALL_NAME_IRELEASEALARM},
|
|
|
|
{0x0020, SYSCALL_NAME_CREATETHREAD},
|
|
|
|
{0x0021, SYSCALL_NAME_DELETETHREAD},
|
|
|
|
{0x0022, SYSCALL_NAME_STARTTHREAD},
|
|
|
|
{0x0023, SYSCALL_NAME_EXITTHREAD},
|
|
|
|
{0x0024, SYSCALL_NAME_EXITDELETETHREAD},
|
|
|
|
{0x0025, SYSCALL_NAME_TERMINATETHREAD},
|
|
|
|
{0x0029, SYSCALL_NAME_CHANGETHREADPRIORITY},
|
|
|
|
{0x002A, SYSCALL_NAME_ICHANGETHREADPRIORITY},
|
|
|
|
{0x002B, SYSCALL_NAME_ROTATETHREADREADYQUEUE},
|
|
|
|
{0x002D, SYSCALL_NAME_RELEASEWAITTHREAD},
|
|
|
|
{0x002E, SYSCALL_NAME_IRELEASEWAITTHREAD},
|
|
|
|
{0x002F, SYSCALL_NAME_GETTHREADID},
|
|
|
|
{0x0030, SYSCALL_NAME_REFERTHREADSTATUS},
|
|
|
|
{0x0031, SYSCALL_NAME_IREFERTHREADSTATUS},
|
|
|
|
{0x0032, SYSCALL_NAME_SLEEPTHREAD},
|
|
|
|
{0x0033, SYSCALL_NAME_WAKEUPTHREAD},
|
|
|
|
{0x0034, SYSCALL_NAME_IWAKEUPTHREAD},
|
|
|
|
{0x0035, SYSCALL_NAME_CANCELWAKEUPTHREAD},
|
|
|
|
{0x0036, SYSCALL_NAME_ICANCELWAKEUPTHREAD},
|
|
|
|
{0x0037, SYSCALL_NAME_SUSPENDTHREAD},
|
|
|
|
{0x0038, SYSCALL_NAME_ISUSPENDTHREAD},
|
|
|
|
{0x0039, SYSCALL_NAME_RESUMETHREAD},
|
2023-08-18 12:40:31 -04:00
|
|
|
{0x003A, SYSCALL_NAME_IRESUMETHREAD},
|
2023-08-18 12:34:46 -04:00
|
|
|
{0x003E, SYSCALL_NAME_ENDOFHEAP},
|
|
|
|
{0x0040, SYSCALL_NAME_CREATESEMA},
|
|
|
|
{0x0041, SYSCALL_NAME_DELETESEMA},
|
|
|
|
{0x0042, SYSCALL_NAME_SIGNALSEMA},
|
|
|
|
{0x0043, SYSCALL_NAME_ISIGNALSEMA},
|
|
|
|
{0x0044, SYSCALL_NAME_WAITSEMA},
|
|
|
|
{0x0045, SYSCALL_NAME_POLLSEMA},
|
|
|
|
{0x0046, SYSCALL_NAME_IPOLLSEMA},
|
|
|
|
{0x0047, SYSCALL_NAME_REFERSEMASTATUS},
|
|
|
|
{0x0048, SYSCALL_NAME_IREFERSEMASTATUS},
|
|
|
|
{0x004B, SYSCALL_NAME_GETOSDCONFIGPARAM},
|
2023-09-21 09:05:03 -04:00
|
|
|
{0x0063, SYSCALL_NAME_GETCOP0},
|
2023-08-18 12:34:46 -04:00
|
|
|
{0x0064, SYSCALL_NAME_FLUSHCACHE},
|
|
|
|
{0x006B, SYSCALL_NAME_SIFSTOPDMA},
|
|
|
|
{0x0070, SYSCALL_NAME_GSGETIMR},
|
|
|
|
{0x0071, SYSCALL_NAME_GSPUTIMR},
|
|
|
|
{0x0073, SYSCALL_NAME_SETVSYNCFLAG},
|
|
|
|
{0x0074, SYSCALL_NAME_SETSYSCALL},
|
|
|
|
{0x0076, SYSCALL_NAME_SIFDMASTAT},
|
|
|
|
{0x0077, SYSCALL_NAME_SIFSETDMA},
|
|
|
|
{0x0078, SYSCALL_NAME_SIFSETDCHAIN},
|
|
|
|
{0x007C, SYSCALL_NAME_DECI2CALL},
|
|
|
|
{0x007E, SYSCALL_NAME_MACHINETYPE},
|
|
|
|
{0x0000, nullptr}
|
|
|
|
};
|
|
|
|
// clang-format on
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
CPS2OS::CPS2OS(CMIPS& ee, uint8* ram, uint8* bios, uint8* spr, CGSHandler*& gs, CSIF& sif, CIopBios& iopBios)
|
|
|
|
: m_ee(ee)
|
|
|
|
, m_gs(gs)
|
|
|
|
, m_ram(ram)
|
|
|
|
, m_bios(bios)
|
|
|
|
, m_spr(spr)
|
|
|
|
, m_sif(sif)
|
2021-11-08 14:11:06 -05:00
|
|
|
, m_libMc2(ram, *this, iopBios)
|
2018-04-30 21:01:23 +01:00
|
|
|
, m_iopBios(iopBios)
|
2024-06-04 17:20:45 -04:00
|
|
|
, m_deci2Handlers(reinterpret_cast<DECI2HANDLER*>(m_ram + BIOS_ADDRESS_DECI2HANDLER_BASE), BIOS_ID_BASE, MAX_DECI2HANDLER)
|
2018-04-30 21:01:23 +01:00
|
|
|
, m_threads(reinterpret_cast<THREAD*>(m_ram + BIOS_ADDRESS_THREAD_BASE), BIOS_ID_BASE, MAX_THREAD)
|
2021-04-22 16:23:27 -04:00
|
|
|
, m_semaphores(reinterpret_cast<SEMAPHORE*>(m_ram + BIOS_ADDRESS_SEMAPHORE_BASE), SEMA_ID_BASE, MAX_SEMAPHORE)
|
2018-04-30 21:01:23 +01:00
|
|
|
, m_intcHandlers(reinterpret_cast<INTCHANDLER*>(m_ram + BIOS_ADDRESS_INTCHANDLER_BASE), BIOS_ID_BASE, MAX_INTCHANDLER)
|
|
|
|
, m_dmacHandlers(reinterpret_cast<DMACHANDLER*>(m_ram + BIOS_ADDRESS_DMACHANDLER_BASE), BIOS_ID_BASE, MAX_DMACHANDLER)
|
|
|
|
, m_alarms(reinterpret_cast<ALARM*>(m_ram + BIOS_ADDRESS_ALARM_BASE), BIOS_ID_BASE, MAX_ALARM)
|
2023-11-10 16:52:50 -05:00
|
|
|
, m_state(reinterpret_cast<BIOS_STATE*>(m_ram + BIOS_ADDRESS_STATE_BASE))
|
2023-11-16 17:58:37 -05:00
|
|
|
, m_currentThreadId(&m_state->currentThreadId)
|
|
|
|
, m_idleThreadId(&m_state->idleThreadId)
|
|
|
|
, m_tlblExceptionHandler(&m_state->tlblExceptionHandler)
|
|
|
|
, m_tlbsExceptionHandler(&m_state->tlbsExceptionHandler)
|
|
|
|
, m_trapExceptionHandler(&m_state->trapExceptionHandler)
|
|
|
|
, m_sifDmaNextIdx(&m_state->sifDmaNextIdx)
|
|
|
|
, m_sifDmaTimes(m_state->sifDmaTimes)
|
|
|
|
, m_threadSchedule(m_threads, &m_state->threadScheduleBase)
|
|
|
|
, m_intcHandlerQueue(m_intcHandlers, &m_state->intcHandlerQueueBase)
|
|
|
|
, m_dmacHandlerQueue(m_dmacHandlers, &m_state->dmacHandlerQueueBase)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2025-04-07 10:35:12 -04:00
|
|
|
static_assert((BIOS_ADDRESS_INTERRUPT_THREAD_CONTEXT + STACKRES) <= BIOS_ADDRESS_DECI2HANDLER_BASE);
|
2019-05-23 21:14:00 -04:00
|
|
|
static_assert((BIOS_ADDRESS_SEMAPHORE_BASE + (sizeof(SEMAPHORE) * MAX_SEMAPHORE)) <= BIOS_ADDRESS_CUSTOMSYSCALL_BASE, "Semaphore overflow");
|
2020-09-09 17:13:17 -04:00
|
|
|
|
|
|
|
CAppConfig::GetInstance().RegisterPreferenceInteger(PREF_SYSTEM_LANGUAGE, static_cast<uint32>(OSD_LANGUAGE::JAPANESE));
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
CPS2OS::~CPS2OS()
|
|
|
|
{
|
|
|
|
Release();
|
|
|
|
}
|
|
|
|
|
2023-06-08 17:12:43 -04:00
|
|
|
void CPS2OS::Initialize(uint32 ramSize)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
m_elf = nullptr;
|
2021-12-09 15:22:19 -05:00
|
|
|
m_idleEvaluator.Reset();
|
2023-06-08 17:12:43 -04:00
|
|
|
m_ramSize = ramSize;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2024-01-04 18:06:02 -05:00
|
|
|
//Specially selected next thread id to make sure main thread id is 1.
|
|
|
|
//First thread to be created is the idle thread, which will cause next id to become 1
|
|
|
|
//for when main thread will be created through SetupThread.
|
|
|
|
m_state->allocateThreadNextId = m_threads.GetIdBase() + MAX_THREAD - 1;
|
|
|
|
|
2015-05-15 19:14:03 -04:00
|
|
|
SetVsyncFlagPtrs(0, 0);
|
2020-08-05 17:43:36 -04:00
|
|
|
UpdateTLBEnabledState();
|
2015-07-03 21:02:25 -04:00
|
|
|
|
|
|
|
AssembleCustomSyscallHandler();
|
|
|
|
AssembleInterruptHandler();
|
|
|
|
AssembleDmacHandler();
|
|
|
|
AssembleIntcHandler();
|
|
|
|
AssembleThreadEpilog();
|
2015-08-06 01:20:04 -04:00
|
|
|
AssembleIdleThreadProc();
|
2015-07-03 21:02:25 -04:00
|
|
|
AssembleAlarmHandler();
|
2015-08-06 01:20:04 -04:00
|
|
|
CreateIdleThread();
|
2015-07-03 21:02:25 -04:00
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
m_ee.m_State.nPC = BIOS_ADDRESS_IDLETHREADPROC;
|
2017-08-25 14:12:58 -04:00
|
|
|
m_ee.m_State.nCOP0[CCOP_SCU::STATUS] |= (CMIPS::STATUS_IE | CMIPS::STATUS_EIE);
|
2019-01-02 21:34:29 -05:00
|
|
|
|
2022-02-10 15:43:34 -05:00
|
|
|
//The BIOS enables TIMER2 and TIMER3 for alarm purposes and others, some games (ex.: SoulCalibur 3, Ape Escape 2) assume timer is counting.
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CTimer::T2_MODE, CTimer::MODE_COUNT_ENABLE | CTimer::MODE_EQUAL_FLAG | CTimer::MODE_OVERFLOW_FLAG);
|
2019-01-02 21:34:29 -05:00
|
|
|
m_ee.m_pMemoryMap->SetWord(CTimer::T3_MODE, CTimer::MODE_CLOCK_SELECT_EXTERNAL | CTimer::MODE_COUNT_ENABLE | CTimer::MODE_EQUAL_FLAG | CTimer::MODE_OVERFLOW_FLAG);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::Release()
|
|
|
|
{
|
|
|
|
UnloadExecutable();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPS2OS::IsIdle() const
|
|
|
|
{
|
2017-08-27 18:43:17 -04:00
|
|
|
return m_ee.CanGenerateInterrupt() &&
|
2021-12-09 15:22:19 -05:00
|
|
|
((m_currentThreadId == m_idleThreadId) || m_idleEvaluator.IsIdle());
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2019-10-16 20:51:11 -04:00
|
|
|
void CPS2OS::BootFromFile(const fs::path& execPath)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2023-04-26 13:33:32 -04:00
|
|
|
auto stream = [&]() -> std::unique_ptr<Framework::CStream> {
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
if(Framework::Android::CContentUtils::IsContentPath(execPath))
|
|
|
|
{
|
|
|
|
auto uri = Framework::Android::CContentUtils::BuildUriFromPath(execPath);
|
|
|
|
return std::make_unique<Framework::Android::CContentStream>(uri.c_str(), "r");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return std::make_unique<Framework::CStdStream>(execPath.native().c_str(), Framework::GetInputStdStreamMode<fs::path::string_type>());
|
|
|
|
}();
|
2018-02-06 19:05:02 -05:00
|
|
|
auto virtualExecutablePath = "host:" + execPath.filename().string();
|
2023-04-26 13:33:32 -04:00
|
|
|
LoadELF(stream.get(), virtualExecutablePath.c_str(), ArgumentList());
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2015-12-02 22:40:54 -05:00
|
|
|
void CPS2OS::BootFromVirtualPath(const char* executablePath, const ArgumentList& arguments)
|
|
|
|
{
|
|
|
|
auto ioman = m_iopBios.GetIoman();
|
|
|
|
|
|
|
|
uint32 handle = ioman->Open(Iop::Ioman::CDevice::OPEN_FLAG_RDONLY, executablePath);
|
|
|
|
if(static_cast<int32>(handle) < 0)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Couldn't open executable specified by virtual path.");
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Framework::CStream* file(ioman->GetFileStream(handle));
|
2023-04-26 13:33:32 -04:00
|
|
|
LoadELF(file, executablePath, arguments);
|
2015-12-02 22:40:54 -05:00
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Error occured while reading ELF executable from virtual path.");
|
|
|
|
}
|
|
|
|
ioman->Close(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::BootFromCDROM()
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
std::string executablePath;
|
2015-12-02 22:40:54 -05:00
|
|
|
auto ioman = m_iopBios.GetIoman();
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
{
|
|
|
|
uint32 handle = ioman->Open(Iop::Ioman::CDevice::OPEN_FLAG_RDONLY, "cdrom0:SYSTEM.CNF");
|
|
|
|
if(static_cast<int32>(handle) < 0)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("No 'SYSTEM.CNF' file found on the cdrom0 device.");
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Framework::CStream* file(ioman->GetFileStream(handle));
|
2015-07-22 00:30:06 -04:00
|
|
|
auto systemConfig = DiskUtils::ParseSystemConfigFile(file);
|
|
|
|
auto bootItemIterator = systemConfig.find("BOOT2");
|
|
|
|
if(bootItemIterator != std::end(systemConfig))
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-07-22 00:30:06 -04:00
|
|
|
executablePath = bootItemIterator->second;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ioman->Close(handle);
|
|
|
|
}
|
|
|
|
|
2024-03-01 15:12:53 -05:00
|
|
|
if(executablePath.empty())
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
throw std::runtime_error("Error parsing 'SYSTEM.CNF' for a BOOT2 value.");
|
|
|
|
}
|
|
|
|
|
2015-12-02 22:40:54 -05:00
|
|
|
BootFromVirtualPath(executablePath.c_str(), ArgumentList());
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2022-07-22 17:01:59 -04:00
|
|
|
CELF32* CPS2OS::GetELF()
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2020-09-09 10:58:50 -04:00
|
|
|
return m_elf.get();
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* CPS2OS::GetExecutableName() const
|
|
|
|
{
|
|
|
|
return m_executableName.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<uint32, uint32> CPS2OS::GetExecutableRange() const
|
|
|
|
{
|
|
|
|
uint32 minAddr = 0xFFFFFFF0;
|
|
|
|
uint32 maxAddr = 0x00000000;
|
2022-03-13 16:04:09 -04:00
|
|
|
const auto& header = m_elf->GetHeader();
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
for(unsigned int i = 0; i < header.nProgHeaderCount; i++)
|
|
|
|
{
|
2022-03-13 16:04:09 -04:00
|
|
|
auto p = m_elf->GetProgram(i);
|
2015-05-06 00:54:15 -04:00
|
|
|
if(p != NULL)
|
|
|
|
{
|
|
|
|
//Wild Arms: Alter Code F has zero sized program headers
|
|
|
|
if(p->nFileSize == 0) continue;
|
2022-07-22 17:01:59 -04:00
|
|
|
if(!(p->nFlags & ELF::PF_X)) continue;
|
2015-05-06 00:54:15 -04:00
|
|
|
uint32 end = p->nVAddress + p->nFileSize;
|
2023-06-08 17:12:43 -04:00
|
|
|
if(end >= m_ramSize) continue;
|
2015-05-06 00:54:15 -04:00
|
|
|
minAddr = std::min<uint32>(minAddr, p->nVAddress);
|
|
|
|
maxAddr = std::max<uint32>(maxAddr, end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::pair<uint32, uint32>(minAddr, maxAddr);
|
|
|
|
}
|
|
|
|
|
2023-04-26 13:33:32 -04:00
|
|
|
void CPS2OS::LoadELF(Framework::CStream* stream, const char* executablePath, const ArgumentList& arguments)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2023-04-26 13:33:32 -04:00
|
|
|
auto elf = std::make_unique<CElf32File>(*stream);
|
2015-07-03 04:58:53 -04:00
|
|
|
const auto& header = elf->GetHeader();
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Check for MIPS CPU
|
2022-07-22 17:01:59 -04:00
|
|
|
if(header.nCPU != ELF::EM_MIPS)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
throw std::runtime_error("Invalid target CPU. Must be MIPS.");
|
|
|
|
}
|
|
|
|
|
2022-07-22 17:01:59 -04:00
|
|
|
if(header.nType != ELF::ET_EXEC)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
throw std::runtime_error("Not an executable ELF file.");
|
|
|
|
}
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
UnloadExecutable();
|
|
|
|
|
2020-09-09 10:58:50 -04:00
|
|
|
m_elf = std::move(elf);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2018-10-11 12:41:37 -04:00
|
|
|
m_currentArguments.clear();
|
|
|
|
m_currentArguments.push_back(executablePath);
|
|
|
|
m_currentArguments.insert(m_currentArguments.end(), arguments.begin(), arguments.end());
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
m_executableName =
|
|
|
|
[&]() {
|
2024-08-05 19:53:34 -04:00
|
|
|
static const char separators[] = {'/', '\\', ':'};
|
|
|
|
auto executableName = executablePath;
|
|
|
|
for(const auto separator : separators)
|
|
|
|
{
|
2024-10-22 12:40:43 +02:00
|
|
|
if(const char* sepPos = strrchr(executablePath, separator))
|
2024-08-05 19:53:34 -04:00
|
|
|
{
|
|
|
|
executableName = std::max(executableName, sepPos + 1);
|
|
|
|
}
|
|
|
|
}
|
2018-04-30 21:01:23 +01:00
|
|
|
return executableName;
|
|
|
|
}();
|
2017-08-26 18:01:48 -04:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
LoadExecutableInternal();
|
2025-01-13 18:15:07 -05:00
|
|
|
ApplyGameConfig();
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
OnExecutableChange();
|
|
|
|
|
2017-08-26 18:01:48 -04:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "Loaded '%s' executable file.\r\n", executablePath);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::LoadExecutableInternal()
|
|
|
|
{
|
|
|
|
//Copy program in main RAM
|
2022-03-13 16:04:09 -04:00
|
|
|
const auto& header = m_elf->GetHeader();
|
2015-05-06 00:54:15 -04:00
|
|
|
for(unsigned int i = 0; i < header.nProgHeaderCount; i++)
|
|
|
|
{
|
|
|
|
auto p = m_elf->GetProgram(i);
|
|
|
|
if(p != nullptr)
|
|
|
|
{
|
2023-01-10 18:41:28 -05:00
|
|
|
if(p->nFileSize == 0) continue;
|
2023-06-08 17:12:43 -04:00
|
|
|
if(p->nVAddress >= m_ramSize)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
memcpy(m_ram + p->nVAddress, m_elf->GetContent() + p->nOffset, p->nFileSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nPC = header.nEntryPoint;
|
2021-06-17 23:05:04 +02:00
|
|
|
m_ee.m_State.nGPR[CMIPS::A0].nV[0] = header.nEntryPoint;
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
std::pair<uint32, uint32> executableRange = GetExecutableRange();
|
|
|
|
uint32 minAddr = executableRange.first;
|
|
|
|
uint32 maxAddr = executableRange.second & ~0x03;
|
|
|
|
|
|
|
|
m_ee.m_analysis->Clear();
|
|
|
|
m_ee.m_analysis->Analyse(minAddr, maxAddr, header.nEntryPoint);
|
|
|
|
|
|
|
|
//Tag system calls
|
|
|
|
for(uint32 address = minAddr; address < maxAddr; address += 4)
|
|
|
|
{
|
|
|
|
//Check for SYSCALL opcode
|
|
|
|
uint32 opcode = *reinterpret_cast<uint32*>(m_ram + address);
|
|
|
|
if(opcode == 0x0000000C)
|
|
|
|
{
|
|
|
|
//Check the opcode before and after it
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 addiu = *reinterpret_cast<uint32*>(m_ram + address - 4);
|
|
|
|
uint32 jr = *reinterpret_cast<uint32*>(m_ram + address + 4);
|
2015-05-06 00:54:15 -04:00
|
|
|
if(
|
2018-04-30 21:01:23 +01:00
|
|
|
(jr == 0x03E00008) &&
|
|
|
|
(addiu & 0xFFFF0000) == 0x24030000)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
//We have it!
|
|
|
|
int16 syscallId = static_cast<int16>(addiu);
|
|
|
|
if(syscallId & 0x8000)
|
|
|
|
{
|
|
|
|
syscallId = 0 - syscallId;
|
|
|
|
}
|
|
|
|
char syscallName[256];
|
|
|
|
int syscallNameIndex = -1;
|
|
|
|
for(int i = 0; g_syscallNames[i].name != NULL; i++)
|
|
|
|
{
|
|
|
|
if(g_syscallNames[i].id == syscallId)
|
|
|
|
{
|
|
|
|
syscallNameIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(syscallNameIndex != -1)
|
|
|
|
{
|
|
|
|
strncpy(syscallName, g_syscallNames[syscallNameIndex].name, 256);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-05-29 06:01:32 +01:00
|
|
|
sprintf(syscallName, "syscall_%04X", syscallId);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
m_ee.m_Functions.InsertTag(address - 4, syscallName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::UnloadExecutable()
|
|
|
|
{
|
2020-09-09 10:58:50 -04:00
|
|
|
if(!m_elf) return;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
OnExecutableUnloading();
|
|
|
|
|
2020-09-09 10:58:50 -04:00
|
|
|
m_elf.reset();
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CPS2OS::LoadExecutable(const char* path, const char* section)
|
|
|
|
{
|
|
|
|
auto ioman = m_iopBios.GetIoman();
|
|
|
|
|
|
|
|
uint32 handle = ioman->Open(Iop::Ioman::CDevice::OPEN_FLAG_RDONLY, path);
|
|
|
|
if(static_cast<int32>(handle) < 0)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 result = 0;
|
|
|
|
|
|
|
|
//We don't support loading anything else than all sections
|
|
|
|
assert(strcmp(section, "all") == 0);
|
|
|
|
|
|
|
|
auto fileStream(ioman->GetFileStream(handle));
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Load all program sections
|
|
|
|
{
|
2022-07-22 17:01:59 -04:00
|
|
|
CElf32File executable(*fileStream);
|
2015-05-06 00:54:15 -04:00
|
|
|
const auto& header = executable.GetHeader();
|
|
|
|
for(unsigned int i = 0; i < header.nProgHeaderCount; i++)
|
|
|
|
{
|
|
|
|
auto p = executable.GetProgram(i);
|
|
|
|
if(p)
|
|
|
|
{
|
|
|
|
memcpy(m_ram + p->nVAddress, executable.GetContent() + p->nOffset, p->nFileSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result = executable.GetHeader().nEntryPoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Flush all instruction cache
|
|
|
|
OnRequestInstructionCacheFlush();
|
|
|
|
|
|
|
|
ioman->Close(handle);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2025-01-13 18:15:07 -05:00
|
|
|
void CPS2OS::ApplyGameConfig()
|
|
|
|
{
|
|
|
|
std::unique_ptr<Framework::Xml::CNode> document;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
Framework::Android::CAssetStream inputStream(GAMECONFIG_FILENAME);
|
|
|
|
#else
|
|
|
|
auto gameConfigPath = Framework::PathUtils::GetAppResourcesPath() / GAMECONFIG_FILENAME;
|
|
|
|
Framework::CStdStream inputStream(Framework::CreateInputStdStream(gameConfigPath.native()));
|
|
|
|
#endif
|
|
|
|
document = Framework::Xml::CParser::ParseDocument(inputStream);
|
|
|
|
if(!document) return;
|
|
|
|
}
|
|
|
|
catch(const std::exception& exception)
|
|
|
|
{
|
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Failed to open game config file: %s.\r\n", exception.what());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto gameConfigsNode = document->Select("GameConfigs");
|
|
|
|
if(!gameConfigsNode)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-01-14 10:53:33 -05:00
|
|
|
CEeExecutor::BlockFpRoundingModeMap blockFpRoundingModes;
|
|
|
|
CEeExecutor::IdleLoopBlockSet idleLoopBlocks;
|
2025-01-13 18:15:07 -05:00
|
|
|
|
|
|
|
for(Framework::Xml::CFilteringNodeIterator itNode(gameConfigsNode, "GameConfig");
|
|
|
|
!itNode.IsEnd(); itNode++)
|
|
|
|
{
|
|
|
|
auto gameConfigNode = (*itNode);
|
|
|
|
|
|
|
|
const char* executable = gameConfigNode->GetAttribute("Executable");
|
|
|
|
if(!executable) continue;
|
|
|
|
|
|
|
|
if(strcmp(executable, GetExecutableName())) continue;
|
|
|
|
|
|
|
|
//Found the right executable, apply config
|
|
|
|
|
2025-01-14 15:30:58 -05:00
|
|
|
for(Framework::Xml::CFilteringNodeIterator itNode(gameConfigNode, "Patch");
|
|
|
|
!itNode.IsEnd(); itNode++)
|
|
|
|
{
|
|
|
|
auto patch = (*itNode);
|
|
|
|
|
|
|
|
const char* addressString = patch->GetAttribute("Address");
|
|
|
|
const char* valueString = patch->GetAttribute("Value");
|
|
|
|
|
|
|
|
if(!addressString) continue;
|
|
|
|
if(!valueString) continue;
|
|
|
|
|
|
|
|
uint32 value = 0, address = 0;
|
|
|
|
if(sscanf(addressString, "%x", &address) == 0) continue;
|
|
|
|
if(sscanf(valueString, "%x", &value) == 0) continue;
|
|
|
|
|
|
|
|
*reinterpret_cast<uint32*>(m_ram + address) = value;
|
|
|
|
}
|
|
|
|
|
2025-01-13 18:15:07 -05:00
|
|
|
for(Framework::Xml::CFilteringNodeIterator itNode(gameConfigNode, "BlockFpRoundingMode");
|
|
|
|
!itNode.IsEnd(); itNode++)
|
|
|
|
{
|
|
|
|
auto node = (*itNode);
|
|
|
|
|
|
|
|
const char* addressString = node->GetAttribute("Address");
|
|
|
|
const char* modeString = node->GetAttribute("Mode");
|
|
|
|
|
|
|
|
if(!addressString) continue;
|
|
|
|
if(!modeString) continue;
|
|
|
|
|
|
|
|
uint32 address = 0;
|
|
|
|
auto roundingMode = Jitter::CJitter::ROUND_NEAREST;
|
|
|
|
if(sscanf(addressString, "%x", &address) == 0) continue;
|
|
|
|
|
|
|
|
if(!strcmp(modeString, "NEAREST"))
|
|
|
|
{
|
|
|
|
roundingMode = Jitter::CJitter::ROUND_NEAREST;
|
|
|
|
}
|
2025-01-15 15:12:25 -05:00
|
|
|
else if(!strcmp(modeString, "PLUSINFINITY"))
|
|
|
|
{
|
|
|
|
roundingMode = Jitter::CJitter::ROUND_PLUSINFINITY;
|
|
|
|
}
|
|
|
|
else if(!strcmp(modeString, "MINUSINFINITY"))
|
|
|
|
{
|
|
|
|
roundingMode = Jitter::CJitter::ROUND_MINUSINFINITY;
|
|
|
|
}
|
|
|
|
else if(!strcmp(modeString, "TRUNCATE"))
|
|
|
|
{
|
|
|
|
roundingMode = Jitter::CJitter::ROUND_TRUNCATE;
|
|
|
|
}
|
2025-01-13 18:15:07 -05:00
|
|
|
else
|
|
|
|
{
|
2025-01-15 15:12:25 -05:00
|
|
|
assert(false);
|
2025-01-13 18:15:07 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
blockFpRoundingModes.insert(std::make_pair(address, roundingMode));
|
|
|
|
}
|
|
|
|
|
2025-01-14 10:53:33 -05:00
|
|
|
for(Framework::Xml::CFilteringNodeIterator itNode(gameConfigNode, "IdleLoopBlock");
|
|
|
|
!itNode.IsEnd(); itNode++)
|
|
|
|
{
|
|
|
|
auto node = (*itNode);
|
|
|
|
|
|
|
|
const char* addressString = node->GetAttribute("Address");
|
|
|
|
if(!addressString) continue;
|
|
|
|
|
|
|
|
uint32 address = 0;
|
|
|
|
if(sscanf(addressString, "%x", &address) == 0) continue;
|
|
|
|
|
|
|
|
idleLoopBlocks.insert(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto executor = static_cast<CEeExecutor*>(m_ee.m_executor.get());
|
|
|
|
executor->SetBlockFpRoundingModes(std::move(blockFpRoundingModes));
|
|
|
|
executor->SetIdleLoopBlocks(std::move(idleLoopBlocks));
|
|
|
|
|
2025-01-13 18:15:07 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::AssembleCustomSyscallHandler()
|
|
|
|
{
|
|
|
|
CMIPSAssembler assembler((uint32*)&m_bios[0x100]);
|
|
|
|
|
|
|
|
//Epilogue
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, 0xFFF0);
|
|
|
|
assembler.SD(CMIPS::RA, 0x0000, CMIPS::SP);
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Load the function address off the table at 0x80010000
|
|
|
|
assembler.SLL(CMIPS::T0, CMIPS::V1, 2);
|
|
|
|
assembler.LUI(CMIPS::T1, 0x8001);
|
|
|
|
assembler.ADDU(CMIPS::T0, CMIPS::T0, CMIPS::T1);
|
|
|
|
assembler.LW(CMIPS::T0, 0x0000, CMIPS::T0);
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//And the address with 0x1FFFFFFF
|
|
|
|
assembler.LUI(CMIPS::T1, 0x1FFF);
|
|
|
|
assembler.ORI(CMIPS::T1, CMIPS::T1, 0xFFFF);
|
|
|
|
assembler.AND(CMIPS::T0, CMIPS::T0, CMIPS::T1);
|
|
|
|
|
|
|
|
//Jump to the system call address
|
|
|
|
assembler.JALR(CMIPS::T0);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Prologue
|
|
|
|
assembler.LD(CMIPS::RA, 0x0000, CMIPS::SP);
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, 0x0010);
|
|
|
|
assembler.ERET();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::AssembleInterruptHandler()
|
|
|
|
{
|
|
|
|
CEEAssembler assembler(reinterpret_cast<uint32*>(&m_bios[BIOS_ADDRESS_INTERRUPTHANDLER - BIOS_ADDRESS_BASE]));
|
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
auto processDmacLabel = assembler.CreateLabel();
|
|
|
|
auto doneLabel = assembler.CreateLabel();
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
const uint32 stackFrameSize = 0x230;
|
|
|
|
|
2017-06-15 10:51:08 -04:00
|
|
|
//Invariant - Current thread is idle thread
|
|
|
|
//This means we don't need to save/load its context because
|
|
|
|
//it doesn't have any
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Epilogue (allocate stackFrameSize bytes)
|
|
|
|
assembler.LI(CMIPS::K0, BIOS_ADDRESS_KERNELSTACK_TOP);
|
|
|
|
assembler.ADDIU(CMIPS::K0, CMIPS::K0, 0x10000 - stackFrameSize);
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Save EPC
|
|
|
|
assembler.MFC0(CMIPS::T0, CCOP_SCU::EPC);
|
|
|
|
assembler.SW(CMIPS::T0, 0x0220, CMIPS::K0);
|
|
|
|
|
|
|
|
//Set SP
|
|
|
|
assembler.ADDU(CMIPS::SP, CMIPS::K0, CMIPS::R0);
|
|
|
|
|
2015-11-01 23:55:20 -05:00
|
|
|
//Disable interrupts. Used by some games (ie.: RE4) to check interrupt context.
|
|
|
|
assembler.MFC0(CMIPS::T0, CCOP_SCU::STATUS);
|
|
|
|
assembler.LI(CMIPS::T1, ~CMIPS::STATUS_IE);
|
|
|
|
assembler.AND(CMIPS::T0, CMIPS::T0, CMIPS::T1);
|
|
|
|
assembler.MTC0(CMIPS::T0, CCOP_SCU::STATUS);
|
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
//Get CAUSE register
|
|
|
|
assembler.MFC0(CMIPS::T0, CCOP_SCU::CAUSE);
|
|
|
|
assembler.ADDIU(CMIPS::T1, CMIPS::R0, CCOP_SCU::CAUSE_IP_3);
|
|
|
|
assembler.AND(CMIPS::T0, CMIPS::T0, CMIPS::T1);
|
|
|
|
assembler.BNE(CMIPS::T0, CMIPS::R0, processDmacLabel);
|
|
|
|
assembler.NOP();
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
//Process INTC interrupt
|
|
|
|
{
|
|
|
|
//Get INTC status
|
|
|
|
assembler.LI(CMIPS::T0, CINTC::INTC_STAT);
|
|
|
|
assembler.LW(CMIPS::S0, 0x0000, CMIPS::T0);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
//Get INTC mask
|
|
|
|
assembler.LI(CMIPS::T1, CINTC::INTC_MASK);
|
|
|
|
assembler.LW(CMIPS::S1, 0x0000, CMIPS::T1);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
//Get cause
|
|
|
|
assembler.AND(CMIPS::S0, CMIPS::S0, CMIPS::S1);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
static const auto generateIntHandler =
|
2021-12-06 08:23:02 -05:00
|
|
|
[](CMIPSAssembler& assembler, uint32 line) {
|
|
|
|
auto skipIntHandlerLabel = assembler.CreateLabel();
|
|
|
|
|
|
|
|
//Check cause
|
|
|
|
assembler.ANDI(CMIPS::T0, CMIPS::S0, (1 << line));
|
|
|
|
assembler.BEQ(CMIPS::R0, CMIPS::T0, skipIntHandlerLabel);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Process handlers
|
|
|
|
assembler.ADDIU(CMIPS::A0, CMIPS::R0, line);
|
|
|
|
assembler.JAL(BIOS_ADDRESS_INTCHANDLER);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
if(line == CINTC::INTC_LINE_TIMER3)
|
|
|
|
{
|
|
|
|
assembler.JAL(BIOS_ADDRESS_ALARMHANDLER);
|
|
|
|
assembler.NOP();
|
|
|
|
}
|
|
|
|
|
|
|
|
assembler.MarkLabel(skipIntHandlerLabel);
|
|
|
|
};
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_GS);
|
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_VBLANK_START);
|
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_VBLANK_END);
|
2022-03-28 17:29:34 -04:00
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_VIF0);
|
2021-12-03 13:44:24 -05:00
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_VIF1);
|
2024-07-31 11:51:44 -04:00
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_VU1);
|
2021-12-03 13:44:24 -05:00
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_IPU);
|
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_TIMER0);
|
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_TIMER1);
|
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_TIMER2);
|
|
|
|
generateIntHandler(assembler, CINTC::INTC_LINE_TIMER3);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
assembler.BEQ(CMIPS::R0, CMIPS::R0, doneLabel);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.NOP();
|
2021-12-03 13:44:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
assembler.MarkLabel(processDmacLabel);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
//Process DMAC interrupt
|
|
|
|
{
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.JAL(BIOS_ADDRESS_DMACHANDLER);
|
|
|
|
assembler.NOP();
|
|
|
|
}
|
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
assembler.MarkLabel(doneLabel);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Make sure interrupts are enabled (This is needed by some games that play
|
|
|
|
//with the status register in interrupt handlers and is done by the EE BIOS)
|
|
|
|
assembler.MFC0(CMIPS::T0, CCOP_SCU::STATUS);
|
|
|
|
assembler.ORI(CMIPS::T0, CMIPS::T0, CMIPS::STATUS_IE);
|
|
|
|
assembler.MTC0(CMIPS::T0, CCOP_SCU::STATUS);
|
|
|
|
|
2019-02-09 11:26:48 -05:00
|
|
|
//Prologue
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Move back SP into K0 before restoring state
|
|
|
|
assembler.ADDIU(CMIPS::K0, CMIPS::SP, CMIPS::R0);
|
2019-02-09 11:26:48 -05:00
|
|
|
//Read EPC from 0x220
|
|
|
|
assembler.LW(CMIPS::A0, 0x0220, CMIPS::K0);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2019-02-09 11:26:48 -05:00
|
|
|
assembler.ADDIU(CMIPS::V1, CMIPS::R0, SYSCALL_CUSTOM_EXITINTERRUPT);
|
|
|
|
assembler.SYSCALL();
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::AssembleDmacHandler()
|
|
|
|
{
|
|
|
|
CMIPSAssembler assembler(reinterpret_cast<uint32*>(&m_bios[BIOS_ADDRESS_DMACHANDLER - BIOS_ADDRESS_BASE]));
|
|
|
|
|
2015-07-26 01:11:43 -04:00
|
|
|
auto checkHandlerLabel = assembler.CreateLabel();
|
2015-05-06 00:54:15 -04:00
|
|
|
auto testChannelLabel = assembler.CreateLabel();
|
|
|
|
auto skipChannelLabel = assembler.CreateLabel();
|
|
|
|
|
2015-07-26 01:11:43 -04:00
|
|
|
auto channelCounterRegister = CMIPS::S0;
|
|
|
|
auto interruptStatusRegister = CMIPS::S1;
|
|
|
|
auto nextIdPtrRegister = CMIPS::S2;
|
|
|
|
|
|
|
|
auto nextIdRegister = CMIPS::T2;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, 0xFFE0);
|
|
|
|
assembler.SD(CMIPS::RA, 0x0000, CMIPS::SP);
|
|
|
|
assembler.SD(CMIPS::S0, 0x0008, CMIPS::SP);
|
|
|
|
assembler.SD(CMIPS::S1, 0x0010, CMIPS::SP);
|
|
|
|
assembler.SD(CMIPS::S2, 0x0018, CMIPS::SP);
|
|
|
|
|
|
|
|
//Load the DMA interrupt status
|
|
|
|
assembler.LI(CMIPS::T0, CDMAC::D_STAT);
|
|
|
|
assembler.LW(CMIPS::T0, 0x0000, CMIPS::T0);
|
|
|
|
|
|
|
|
assembler.SRL(CMIPS::T1, CMIPS::T0, 16);
|
2015-07-26 01:11:43 -04:00
|
|
|
assembler.AND(interruptStatusRegister, CMIPS::T0, CMIPS::T1);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Initialize channel counter
|
2022-02-22 10:04:38 -05:00
|
|
|
assembler.ADDIU(channelCounterRegister, CMIPS::R0, 0x000E);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
assembler.MarkLabel(testChannelLabel);
|
|
|
|
|
|
|
|
//Check if that specific DMA channel interrupt is the cause
|
|
|
|
assembler.ORI(CMIPS::T0, CMIPS::R0, 0x0001);
|
|
|
|
assembler.SLLV(CMIPS::T0, CMIPS::T0, CMIPS::S0);
|
|
|
|
assembler.AND(CMIPS::T0, CMIPS::T0, CMIPS::S1);
|
|
|
|
assembler.BEQ(CMIPS::T0, CMIPS::R0, skipChannelLabel);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Clear interrupt
|
|
|
|
assembler.LI(CMIPS::T1, CDMAC::D_STAT);
|
|
|
|
assembler.SW(CMIPS::T0, 0x0000, CMIPS::T1);
|
|
|
|
|
|
|
|
//Initialize DMAC handler loop
|
2023-11-16 17:58:37 -05:00
|
|
|
assembler.LI(nextIdPtrRegister, BIOS_ADDRESS_STATE_ITEM(dmacHandlerQueueBase));
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-26 01:11:43 -04:00
|
|
|
assembler.MarkLabel(checkHandlerLabel);
|
|
|
|
|
|
|
|
//Check
|
|
|
|
assembler.LW(nextIdRegister, 0, nextIdPtrRegister);
|
|
|
|
assembler.BEQ(nextIdRegister, CMIPS::R0, skipChannelLabel);
|
|
|
|
assembler.ADDIU(nextIdRegister, nextIdRegister, static_cast<uint16>(-1));
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Get the address to the current DMACHANDLER structure
|
|
|
|
assembler.ADDIU(CMIPS::T0, CMIPS::R0, sizeof(DMACHANDLER));
|
2015-07-26 01:11:43 -04:00
|
|
|
assembler.MULTU(CMIPS::T0, nextIdRegister, CMIPS::T0);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.LI(CMIPS::T1, BIOS_ADDRESS_DMACHANDLER_BASE);
|
|
|
|
assembler.ADDU(CMIPS::T0, CMIPS::T0, CMIPS::T1);
|
|
|
|
|
2015-07-26 01:11:43 -04:00
|
|
|
//Adjust nextIdPtr
|
|
|
|
assembler.ADDIU(nextIdPtrRegister, CMIPS::T0, offsetof(INTCHANDLER, nextId));
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Check if the channel is good one
|
|
|
|
assembler.LW(CMIPS::T1, offsetof(DMACHANDLER, channel), CMIPS::T0);
|
2015-07-26 01:11:43 -04:00
|
|
|
assembler.BNE(channelCounterRegister, CMIPS::T1, checkHandlerLabel);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Load the necessary stuff
|
|
|
|
assembler.LW(CMIPS::T1, offsetof(DMACHANDLER, address), CMIPS::T0);
|
2015-07-26 01:11:43 -04:00
|
|
|
assembler.ADDU(CMIPS::A0, channelCounterRegister, CMIPS::R0);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.LW(CMIPS::A1, offsetof(DMACHANDLER, arg), CMIPS::T0);
|
|
|
|
assembler.LW(CMIPS::GP, offsetof(DMACHANDLER, gp), CMIPS::T0);
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Jump
|
|
|
|
assembler.JALR(CMIPS::T1);
|
|
|
|
assembler.NOP();
|
|
|
|
|
2015-07-26 01:11:43 -04:00
|
|
|
assembler.BGEZ(CMIPS::V0, checkHandlerLabel);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
assembler.MarkLabel(skipChannelLabel);
|
|
|
|
|
|
|
|
//Decrement channel counter and test
|
2015-07-26 01:11:43 -04:00
|
|
|
assembler.ADDIU(channelCounterRegister, channelCounterRegister, 0xFFFF);
|
|
|
|
assembler.BGEZ(channelCounterRegister, testChannelLabel);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Epilogue
|
|
|
|
assembler.LD(CMIPS::RA, 0x0000, CMIPS::SP);
|
|
|
|
assembler.LD(CMIPS::S0, 0x0008, CMIPS::SP);
|
|
|
|
assembler.LD(CMIPS::S1, 0x0010, CMIPS::SP);
|
|
|
|
assembler.LD(CMIPS::S2, 0x0018, CMIPS::SP);
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, 0x20);
|
|
|
|
assembler.JR(CMIPS::RA);
|
|
|
|
assembler.NOP();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::AssembleIntcHandler()
|
|
|
|
{
|
2015-07-25 20:13:59 -04:00
|
|
|
CMIPSAssembler assembler(reinterpret_cast<uint32*>(&m_bios[BIOS_ADDRESS_INTCHANDLER - BIOS_ADDRESS_BASE]));
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
auto checkHandlerLabel = assembler.CreateLabel();
|
|
|
|
auto finishLoop = assembler.CreateLabel();
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
auto nextIdPtrRegister = CMIPS::S0;
|
|
|
|
auto causeRegister = CMIPS::S1;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
auto nextIdRegister = CMIPS::T2;
|
|
|
|
|
|
|
|
//Prologue
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, 0xFFE0);
|
|
|
|
assembler.SD(CMIPS::RA, 0x0000, CMIPS::SP);
|
|
|
|
assembler.SD(CMIPS::S0, 0x0008, CMIPS::SP);
|
|
|
|
assembler.SD(CMIPS::S1, 0x0010, CMIPS::SP);
|
|
|
|
|
|
|
|
//Clear INTC cause
|
2015-07-25 20:13:59 -04:00
|
|
|
assembler.LI(CMIPS::T1, CINTC::INTC_STAT);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.ADDIU(CMIPS::T0, CMIPS::R0, 0x0001);
|
|
|
|
assembler.SLLV(CMIPS::T0, CMIPS::T0, CMIPS::A0);
|
|
|
|
assembler.SW(CMIPS::T0, 0x0000, CMIPS::T1);
|
|
|
|
|
|
|
|
//Initialize INTC handler loop
|
2023-11-16 17:58:37 -05:00
|
|
|
assembler.LI(nextIdPtrRegister, BIOS_ADDRESS_STATE_ITEM(intcHandlerQueueBase));
|
2015-07-25 20:13:59 -04:00
|
|
|
assembler.ADDU(causeRegister, CMIPS::A0, CMIPS::R0);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
assembler.MarkLabel(checkHandlerLabel);
|
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
//Check
|
|
|
|
assembler.LW(nextIdRegister, 0, nextIdPtrRegister);
|
|
|
|
assembler.BEQ(nextIdRegister, CMIPS::R0, finishLoop);
|
|
|
|
assembler.ADDIU(nextIdRegister, nextIdRegister, static_cast<uint16>(-1));
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Get the address to the current INTCHANDLER structure
|
|
|
|
assembler.ADDIU(CMIPS::T0, CMIPS::R0, sizeof(INTCHANDLER));
|
2015-07-25 20:13:59 -04:00
|
|
|
assembler.MULTU(CMIPS::T0, nextIdRegister, CMIPS::T0);
|
|
|
|
assembler.LI(CMIPS::T1, BIOS_ADDRESS_INTCHANDLER_BASE);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.ADDU(CMIPS::T0, CMIPS::T0, CMIPS::T1);
|
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
//Adjust nextIdPtr
|
|
|
|
assembler.ADDIU(nextIdPtrRegister, CMIPS::T0, offsetof(INTCHANDLER, nextId));
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Check if the cause is good one
|
2015-07-25 20:13:59 -04:00
|
|
|
assembler.LW(CMIPS::T1, offsetof(INTCHANDLER, cause), CMIPS::T0);
|
|
|
|
assembler.BNE(causeRegister, CMIPS::T1, checkHandlerLabel);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Load the necessary stuff
|
2015-07-25 20:13:59 -04:00
|
|
|
assembler.LW(CMIPS::T1, offsetof(INTCHANDLER, address), CMIPS::T0);
|
|
|
|
assembler.ADDU(CMIPS::A0, causeRegister, CMIPS::R0);
|
|
|
|
assembler.LW(CMIPS::A1, offsetof(INTCHANDLER, arg), CMIPS::T0);
|
|
|
|
assembler.LW(CMIPS::GP, offsetof(INTCHANDLER, gp), CMIPS::T0);
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Jump
|
|
|
|
assembler.JALR(CMIPS::T1);
|
|
|
|
assembler.NOP();
|
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
assembler.BGEZ(CMIPS::V0, checkHandlerLabel);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.NOP();
|
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
assembler.MarkLabel(finishLoop);
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Epilogue
|
|
|
|
assembler.LD(CMIPS::RA, 0x0000, CMIPS::SP);
|
|
|
|
assembler.LD(CMIPS::S0, 0x0008, CMIPS::SP);
|
|
|
|
assembler.LD(CMIPS::S1, 0x0010, CMIPS::SP);
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, 0x20);
|
|
|
|
assembler.JR(CMIPS::RA);
|
|
|
|
assembler.NOP();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::AssembleThreadEpilog()
|
|
|
|
{
|
|
|
|
CMIPSAssembler assembler((uint32*)&m_bios[BIOS_ADDRESS_THREADEPILOG - BIOS_ADDRESS_BASE]);
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.ADDIU(CMIPS::V1, CMIPS::R0, 0x23);
|
|
|
|
assembler.SYSCALL();
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
void CPS2OS::AssembleIdleThreadProc()
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-06 01:20:04 -04:00
|
|
|
CMIPSAssembler assembler((uint32*)&m_bios[BIOS_ADDRESS_IDLETHREADPROC - BIOS_ADDRESS_BASE]);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2019-02-09 11:16:59 -05:00
|
|
|
assembler.ADDIU(CMIPS::V1, CMIPS::R0, SYSCALL_CUSTOM_RESCHEDULE);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.SYSCALL();
|
|
|
|
|
|
|
|
assembler.BEQ(CMIPS::R0, CMIPS::R0, 0xFFFD);
|
|
|
|
assembler.NOP();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::AssembleAlarmHandler()
|
|
|
|
{
|
|
|
|
CMIPSAssembler assembler(reinterpret_cast<uint32*>(&m_bios[BIOS_ADDRESS_ALARMHANDLER - BIOS_ADDRESS_BASE]));
|
|
|
|
|
|
|
|
auto checkHandlerLabel = assembler.CreateLabel();
|
|
|
|
auto moveToNextHandler = assembler.CreateLabel();
|
|
|
|
|
|
|
|
//Prologue
|
|
|
|
//S0 -> Handler Counter
|
2021-10-01 11:03:14 -04:00
|
|
|
//S1 -> Compare Value
|
|
|
|
int32 stackAlloc = 0x20;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-10-01 11:03:14 -04:00
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, -stackAlloc);
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.SD(CMIPS::RA, 0x0000, CMIPS::SP);
|
|
|
|
assembler.SD(CMIPS::S0, 0x0008, CMIPS::SP);
|
2021-10-01 11:03:14 -04:00
|
|
|
assembler.SD(CMIPS::S1, 0x0010, CMIPS::SP);
|
|
|
|
|
|
|
|
//Load T3 compare
|
|
|
|
assembler.LI(CMIPS::S1, CTimer::T3_COMP);
|
|
|
|
assembler.LW(CMIPS::S1, 0, CMIPS::S1);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Initialize handler loop
|
|
|
|
assembler.ADDU(CMIPS::S0, CMIPS::R0, CMIPS::R0);
|
|
|
|
|
|
|
|
assembler.MarkLabel(checkHandlerLabel);
|
|
|
|
|
2021-10-01 11:03:14 -04:00
|
|
|
//Get the address to the current ALARM structure
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.ADDIU(CMIPS::T0, CMIPS::R0, sizeof(ALARM));
|
|
|
|
assembler.MULTU(CMIPS::T0, CMIPS::S0, CMIPS::T0);
|
|
|
|
assembler.LI(CMIPS::T1, BIOS_ADDRESS_ALARM_BASE);
|
|
|
|
assembler.ADDU(CMIPS::T0, CMIPS::T0, CMIPS::T1);
|
|
|
|
|
|
|
|
//Check validity
|
|
|
|
assembler.LW(CMIPS::T1, offsetof(ALARM, isValid), CMIPS::T0);
|
|
|
|
assembler.BEQ(CMIPS::T1, CMIPS::R0, moveToNextHandler);
|
|
|
|
assembler.NOP();
|
|
|
|
|
2021-10-01 11:03:14 -04:00
|
|
|
assembler.LW(CMIPS::T1, offsetof(ALARM, compare), CMIPS::T0);
|
|
|
|
assembler.ORI(CMIPS::T2, CMIPS::R0, 0xFFFF);
|
|
|
|
assembler.AND(CMIPS::T1, CMIPS::T1, CMIPS::T2);
|
|
|
|
|
|
|
|
assembler.BNE(CMIPS::T1, CMIPS::S1, moveToNextHandler);
|
|
|
|
assembler.NOP();
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Load the necessary stuff
|
|
|
|
assembler.LW(CMIPS::T1, offsetof(ALARM, callback), CMIPS::T0);
|
|
|
|
assembler.ADDIU(CMIPS::A0, CMIPS::S0, BIOS_ID_BASE);
|
|
|
|
assembler.LW(CMIPS::A1, offsetof(ALARM, delay), CMIPS::T0);
|
|
|
|
assembler.LW(CMIPS::A2, offsetof(ALARM, callbackParam), CMIPS::T0);
|
|
|
|
assembler.LW(CMIPS::GP, offsetof(ALARM, gp), CMIPS::T0);
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Jump
|
|
|
|
assembler.JALR(CMIPS::T1);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Delete handler (call iReleaseAlarm)
|
|
|
|
assembler.ADDIU(CMIPS::A0, CMIPS::S0, BIOS_ID_BASE);
|
|
|
|
assembler.ADDIU(CMIPS::V1, CMIPS::R0, -0x1F);
|
|
|
|
assembler.SYSCALL();
|
|
|
|
|
|
|
|
assembler.MarkLabel(moveToNextHandler);
|
|
|
|
|
|
|
|
//Increment handler counter and test
|
|
|
|
assembler.ADDIU(CMIPS::S0, CMIPS::S0, 0x0001);
|
|
|
|
assembler.ADDIU(CMIPS::T0, CMIPS::R0, MAX_ALARM - 1);
|
|
|
|
assembler.BNE(CMIPS::S0, CMIPS::T0, checkHandlerLabel);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Epilogue
|
|
|
|
assembler.LD(CMIPS::RA, 0x0000, CMIPS::SP);
|
|
|
|
assembler.LD(CMIPS::S0, 0x0008, CMIPS::SP);
|
2021-10-01 11:03:14 -04:00
|
|
|
assembler.LD(CMIPS::S1, 0x0010, CMIPS::SP);
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, stackAlloc);
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
assembler.JR(CMIPS::RA);
|
|
|
|
assembler.NOP();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32* CPS2OS::GetCustomSyscallTable()
|
|
|
|
{
|
2019-05-23 21:13:37 -04:00
|
|
|
return (uint32*)&m_ram[BIOS_ADDRESS_CUSTOMSYSCALL_BASE];
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2015-08-09 00:03:42 -04:00
|
|
|
void CPS2OS::LinkThread(uint32 threadId)
|
|
|
|
{
|
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
|
|
|
|
for(auto threadSchedulePair : m_threadSchedule)
|
|
|
|
{
|
|
|
|
auto scheduledThread = threadSchedulePair.second;
|
|
|
|
if(scheduledThread->currPriority > thread->currPriority)
|
|
|
|
{
|
|
|
|
m_threadSchedule.AddBefore(threadSchedulePair.first, threadId);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_threadSchedule.PushBack(threadId);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::UnlinkThread(uint32 threadId)
|
|
|
|
{
|
|
|
|
m_threadSchedule.Unlink(threadId);
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::ThreadShakeAndBake()
|
|
|
|
{
|
|
|
|
//Don't play with fire (don't switch if we're in exception mode)
|
|
|
|
if(m_ee.m_State.nCOP0[CCOP_SCU::STATUS] & CMIPS::STATUS_EXL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Don't switch if interrupts are disabled
|
|
|
|
if((m_ee.m_State.nCOP0[CCOP_SCU::STATUS] & INTERRUPTS_ENABLED_MASK) != INTERRUPTS_ENABLED_MASK)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-04 17:49:21 -04:00
|
|
|
//SetupThread has not been called, not ready to switch threads yet
|
|
|
|
if(m_currentThreadId == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Select thread to execute
|
|
|
|
{
|
2015-08-09 00:03:42 -04:00
|
|
|
uint32 nextThreadId = 0;
|
|
|
|
if(m_threadSchedule.IsEmpty())
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-04 01:16:43 -04:00
|
|
|
//No thread ready to be executed
|
2015-08-09 00:03:42 -04:00
|
|
|
nextThreadId = m_idleThreadId;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-08-09 00:03:42 -04:00
|
|
|
auto nextThreadPair = *m_threadSchedule.begin();
|
|
|
|
nextThreadId = nextThreadPair.first;
|
|
|
|
assert(nextThreadPair.second->status == THREAD_RUNNING);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
2015-08-09 00:03:42 -04:00
|
|
|
ThreadSwitchContext(nextThreadId);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-20 15:36:48 -05:00
|
|
|
void CPS2OS::ThreadSwitchContext(uint32 id)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
if(id == m_currentThreadId) return;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Save the context of the current thread
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
auto thread = m_threads[m_currentThreadId];
|
2015-08-06 01:20:04 -04:00
|
|
|
assert(thread);
|
2017-06-15 10:24:17 -04:00
|
|
|
thread->epc = m_ee.m_State.nPC;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2017-06-15 10:24:17 -04:00
|
|
|
//Idle thread doesn't have a context
|
|
|
|
if(m_currentThreadId != m_idleThreadId)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2019-02-09 11:26:48 -05:00
|
|
|
ThreadSaveContext(thread, false);
|
2017-06-15 10:24:17 -04:00
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2015-08-08 23:36:00 -04:00
|
|
|
m_currentThreadId = id;
|
2021-12-09 15:22:19 -05:00
|
|
|
m_idleEvaluator.NotifyEvent(Ee::CIdleEvaluator::EVENT_CHANGETHREAD, id);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//Load the new context
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
auto thread = m_threads[m_currentThreadId];
|
2015-08-06 01:20:04 -04:00
|
|
|
assert(thread);
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nPC = thread->epc;
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
//Idle thread doesn't have a context
|
|
|
|
if(id != m_idleThreadId)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2019-02-09 11:26:48 -05:00
|
|
|
ThreadLoadContext(thread, false);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CLog::GetInstance().Print(LOG_NAME, "New thread elected (id = %i).\r\n", id);
|
|
|
|
}
|
|
|
|
|
2019-02-09 11:26:48 -05:00
|
|
|
void CPS2OS::ThreadSaveContext(THREAD* thread, bool interrupt)
|
|
|
|
{
|
|
|
|
if(interrupt)
|
|
|
|
{
|
|
|
|
thread->contextPtr = BIOS_ADDRESS_INTERRUPT_THREAD_CONTEXT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
thread->contextPtr = m_ee.m_State.nGPR[CMIPS::SP].nV0 - STACKRES;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto context = reinterpret_cast<THREADCONTEXT*>(GetStructPtr(thread->contextPtr));
|
|
|
|
|
|
|
|
//Save the context
|
|
|
|
for(uint32 i = 0; i < 0x20; i++)
|
|
|
|
{
|
|
|
|
if(i == CMIPS::R0) continue;
|
|
|
|
if(i == CMIPS::K0) continue;
|
|
|
|
if(i == CMIPS::K1) continue;
|
|
|
|
context->gpr[i] = m_ee.m_State.nGPR[i];
|
|
|
|
}
|
|
|
|
for(uint32 i = 0; i < 0x20; i++)
|
|
|
|
{
|
|
|
|
context->cop1[i] = m_ee.m_State.nCOP1[i];
|
|
|
|
}
|
|
|
|
auto& sa = context->gpr[CMIPS::R0].nV0;
|
|
|
|
auto& hi = context->gpr[CMIPS::K0];
|
|
|
|
auto& lo = context->gpr[CMIPS::K1];
|
|
|
|
sa = m_ee.m_State.nSA >> 3; //Act as if MFSA was used
|
|
|
|
hi.nV[0] = m_ee.m_State.nHI[0];
|
|
|
|
hi.nV[1] = m_ee.m_State.nHI[1];
|
|
|
|
hi.nV[2] = m_ee.m_State.nHI1[0];
|
|
|
|
hi.nV[3] = m_ee.m_State.nHI1[1];
|
|
|
|
lo.nV[0] = m_ee.m_State.nLO[0];
|
|
|
|
lo.nV[1] = m_ee.m_State.nLO[1];
|
|
|
|
lo.nV[2] = m_ee.m_State.nLO1[0];
|
|
|
|
lo.nV[3] = m_ee.m_State.nLO1[1];
|
|
|
|
context->cop1a = m_ee.m_State.nCOP1A;
|
|
|
|
context->fcsr = m_ee.m_State.nFCSR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::ThreadLoadContext(THREAD* thread, bool interrupt)
|
|
|
|
{
|
|
|
|
assert(thread->contextPtr != 0);
|
2019-02-21 09:14:03 -05:00
|
|
|
assert(!interrupt || (thread->contextPtr == BIOS_ADDRESS_INTERRUPT_THREAD_CONTEXT));
|
2019-02-09 11:26:48 -05:00
|
|
|
|
|
|
|
auto context = reinterpret_cast<const THREADCONTEXT*>(GetStructPtr(thread->contextPtr));
|
|
|
|
for(uint32 i = 0; i < 0x20; i++)
|
|
|
|
{
|
|
|
|
if(i == CMIPS::R0) continue;
|
|
|
|
if(i == CMIPS::K0) continue;
|
|
|
|
if(i == CMIPS::K1) continue;
|
|
|
|
m_ee.m_State.nGPR[i] = context->gpr[i];
|
|
|
|
}
|
|
|
|
for(uint32 i = 0; i < 0x20; i++)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nCOP1[i] = context->cop1[i];
|
|
|
|
}
|
|
|
|
auto& sa = context->gpr[CMIPS::R0].nV0;
|
|
|
|
auto& hi = context->gpr[CMIPS::K0];
|
|
|
|
auto& lo = context->gpr[CMIPS::K1];
|
|
|
|
m_ee.m_State.nSA = (sa & 0x0F) << 3; //Act as if MTSA was used
|
|
|
|
m_ee.m_State.nHI[0] = hi.nV[0];
|
|
|
|
m_ee.m_State.nHI[1] = hi.nV[1];
|
|
|
|
m_ee.m_State.nHI1[0] = hi.nV[2];
|
|
|
|
m_ee.m_State.nHI1[1] = hi.nV[3];
|
|
|
|
m_ee.m_State.nLO[0] = lo.nV[0];
|
|
|
|
m_ee.m_State.nLO[1] = lo.nV[1];
|
|
|
|
m_ee.m_State.nLO1[0] = lo.nV[2];
|
|
|
|
m_ee.m_State.nLO1[1] = lo.nV[3];
|
|
|
|
m_ee.m_State.nCOP1A = context->cop1a;
|
|
|
|
m_ee.m_State.nFCSR = context->fcsr;
|
|
|
|
}
|
|
|
|
|
2016-11-20 17:09:58 -05:00
|
|
|
void CPS2OS::ThreadReset(uint32 id)
|
|
|
|
{
|
|
|
|
assert(m_currentThreadId != id);
|
|
|
|
|
|
|
|
auto thread = m_threads[id];
|
|
|
|
assert(thread->status == THREAD_ZOMBIE);
|
|
|
|
|
|
|
|
uint32 stackTop = thread->stackBase + thread->stackSize;
|
|
|
|
|
|
|
|
thread->contextPtr = stackTop - STACKRES;
|
|
|
|
thread->currPriority = thread->initPriority;
|
|
|
|
//Reset wakeup count?
|
|
|
|
|
|
|
|
auto context = reinterpret_cast<THREADCONTEXT*>(GetStructPtr(thread->contextPtr));
|
|
|
|
context->gpr[CMIPS::SP].nV0 = stackTop - STACK_FRAME_RESERVE_SIZE;
|
|
|
|
context->gpr[CMIPS::FP].nV0 = stackTop - STACK_FRAME_RESERVE_SIZE;
|
|
|
|
context->gpr[CMIPS::GP].nV0 = thread->gp;
|
|
|
|
context->gpr[CMIPS::RA].nV0 = BIOS_ADDRESS_THREADEPILOG;
|
|
|
|
}
|
|
|
|
|
2015-10-27 00:22:51 -04:00
|
|
|
void CPS2OS::CheckLivingThreads()
|
|
|
|
{
|
|
|
|
//Check if we have a living thread (this is needed for autotests to work properly)
|
|
|
|
bool hasLiveThread = false;
|
|
|
|
for(auto thread : m_threads)
|
|
|
|
{
|
|
|
|
if(!thread) continue;
|
|
|
|
if(thread->status != THREAD_ZOMBIE)
|
|
|
|
{
|
|
|
|
hasLiveThread = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!hasLiveThread)
|
|
|
|
{
|
|
|
|
OnRequestExit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 19:11:29 -04:00
|
|
|
void CPS2OS::SemaLinkThread(uint32 semaId, uint32 threadId)
|
|
|
|
{
|
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
auto sema = m_semaphores[semaId];
|
|
|
|
|
|
|
|
assert(sema);
|
|
|
|
|
|
|
|
assert(thread);
|
2021-07-23 09:14:49 -04:00
|
|
|
assert(thread->semaWait == 0);
|
2021-07-22 19:11:29 -04:00
|
|
|
assert(thread->nextId == 0);
|
|
|
|
|
|
|
|
uint32* waitNextId = &sema->waitNextId;
|
|
|
|
while((*waitNextId) != 0)
|
|
|
|
{
|
|
|
|
auto waitThread = m_threads[*waitNextId];
|
|
|
|
waitNextId = &waitThread->nextId;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*waitNextId) = threadId;
|
2021-07-23 09:14:49 -04:00
|
|
|
thread->semaWait = semaId;
|
|
|
|
|
|
|
|
sema->waitCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::SemaUnlinkThread(uint32 semaId, uint32 threadId)
|
|
|
|
{
|
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
auto sema = m_semaphores[semaId];
|
|
|
|
|
|
|
|
assert(sema);
|
|
|
|
assert(sema->waitCount != 0);
|
|
|
|
assert(sema->waitNextId != 0);
|
|
|
|
|
|
|
|
assert(thread);
|
|
|
|
assert(thread->semaWait == semaId);
|
|
|
|
|
|
|
|
uint32* waitNextId = &sema->waitNextId;
|
|
|
|
while((*waitNextId) != 0)
|
|
|
|
{
|
|
|
|
if((*waitNextId) == threadId)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
auto waitThread = m_threads[*waitNextId];
|
|
|
|
waitNextId = &waitThread->nextId;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert((*waitNextId) != 0);
|
|
|
|
(*waitNextId) = thread->nextId;
|
|
|
|
thread->nextId = 0;
|
|
|
|
thread->semaWait = 0;
|
|
|
|
|
|
|
|
sema->waitCount--;
|
2021-07-22 19:11:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::SemaReleaseSingleThread(uint32 semaId, bool cancelled)
|
2017-01-01 18:29:32 -05:00
|
|
|
{
|
|
|
|
//Releases a single thread from a semaphore's queue
|
|
|
|
|
|
|
|
auto sema = m_semaphores[semaId];
|
|
|
|
assert(sema);
|
|
|
|
assert(sema->waitCount != 0);
|
2021-07-22 19:11:29 -04:00
|
|
|
assert(sema->waitNextId != 0);
|
2017-01-01 18:29:32 -05:00
|
|
|
|
2021-07-22 19:11:29 -04:00
|
|
|
uint32 threadId = sema->waitNextId;
|
2021-07-23 09:24:38 -04:00
|
|
|
|
2021-07-22 19:11:29 -04:00
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
assert(thread);
|
|
|
|
assert(thread->semaWait == semaId);
|
|
|
|
|
|
|
|
//Unlink thread
|
|
|
|
sema->waitNextId = thread->nextId;
|
|
|
|
thread->nextId = 0;
|
2021-07-23 09:14:49 -04:00
|
|
|
thread->semaWait = 0;
|
2017-01-01 18:29:32 -05:00
|
|
|
|
2021-07-23 09:24:38 -04:00
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_WAITING:
|
|
|
|
thread->status = THREAD_RUNNING;
|
2021-07-22 19:11:29 -04:00
|
|
|
LinkThread(threadId);
|
2021-07-23 09:24:38 -04:00
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED_WAITING:
|
|
|
|
thread->status = THREAD_SUSPENDED;
|
|
|
|
break;
|
|
|
|
default:
|
2021-07-22 19:11:29 -04:00
|
|
|
//Invalid thread state
|
2021-07-23 09:24:38 -04:00
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
2017-01-01 18:29:32 -05:00
|
|
|
|
2021-07-22 19:11:29 -04:00
|
|
|
uint32 returnValue = cancelled ? -1 : semaId;
|
2021-07-23 09:24:38 -04:00
|
|
|
auto context = reinterpret_cast<THREADCONTEXT*>(GetStructPtr(thread->contextPtr));
|
|
|
|
context->gpr[SC_RETURN].nD0 = static_cast<int32>(returnValue);
|
2017-01-01 18:29:32 -05:00
|
|
|
|
2021-07-23 09:24:38 -04:00
|
|
|
sema->waitCount--;
|
2017-01-01 18:29:32 -05:00
|
|
|
}
|
|
|
|
|
2021-10-01 11:03:14 -04:00
|
|
|
void CPS2OS::AlarmUpdateCompare()
|
|
|
|
{
|
|
|
|
uint32 minCompare = UINT32_MAX;
|
|
|
|
for(const auto& alarm : m_alarms)
|
|
|
|
{
|
|
|
|
if(!alarm) continue;
|
|
|
|
minCompare = std::min<uint32>(alarm->compare, minCompare);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(minCompare == UINT32_MAX)
|
|
|
|
{
|
|
|
|
//No alarm to watch
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Enable TIMER3 INTs
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CTimer::T3_MODE, CTimer::MODE_CLOCK_SELECT_EXTERNAL | CTimer::MODE_COUNT_ENABLE | CTimer::MODE_EQUAL_FLAG | CTimer::MODE_EQUAL_INT_ENABLE);
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CTimer::T3_COMP, minCompare & 0xFFFF);
|
|
|
|
|
|
|
|
uint32 mask = (1 << CINTC::INTC_LINE_TIMER3);
|
|
|
|
if(!(m_ee.m_pMemoryMap->GetWord(CINTC::INTC_MASK) & mask))
|
|
|
|
{
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CINTC::INTC_MASK, mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
void CPS2OS::CreateIdleThread()
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2023-11-16 17:58:37 -05:00
|
|
|
m_idleThreadId = m_threads.AllocateAt(m_state->allocateThreadNextId);
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[m_idleThreadId];
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->epc = BIOS_ADDRESS_IDLETHREADPROC;
|
|
|
|
thread->status = THREAD_ZOMBIE;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2015-05-15 19:14:03 -04:00
|
|
|
std::pair<uint32, uint32> CPS2OS::GetVsyncFlagPtrs() const
|
|
|
|
{
|
2023-11-16 17:58:37 -05:00
|
|
|
return std::make_pair(m_state->vsyncFlagValue1Ptr, m_state->vsyncFlagValue2Ptr);
|
2015-05-15 19:14:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::SetVsyncFlagPtrs(uint32 value1Ptr, uint32 value2Ptr)
|
|
|
|
{
|
2023-11-16 17:58:37 -05:00
|
|
|
m_state->vsyncFlagValue1Ptr = value1Ptr;
|
|
|
|
m_state->vsyncFlagValue2Ptr = value2Ptr;
|
2015-05-15 19:14:03 -04:00
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
uint8* CPS2OS::GetStructPtr(uint32 address) const
|
|
|
|
{
|
2015-12-02 22:22:30 -05:00
|
|
|
address = TranslateAddress(nullptr, address);
|
2015-05-06 00:54:15 -04:00
|
|
|
uint8* memory = nullptr;
|
2015-12-02 22:22:30 -05:00
|
|
|
if((address >= PS2::EE_SPR_ADDR) && (address < (PS2::EE_SPR_ADDR + PS2::EE_SPR_SIZE)))
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
address &= (PS2::EE_SPR_SIZE - 1);
|
|
|
|
memory = m_spr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-06-08 17:12:43 -04:00
|
|
|
address &= (m_ramSize - 1);
|
2015-05-06 00:54:15 -04:00
|
|
|
memory = m_ram;
|
|
|
|
}
|
|
|
|
return memory + address;
|
|
|
|
}
|
|
|
|
|
2021-11-08 14:11:06 -05:00
|
|
|
Ee::CLibMc2& CPS2OS::GetLibMc2()
|
|
|
|
{
|
|
|
|
return m_libMc2;
|
|
|
|
}
|
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
void CPS2OS::HandleInterrupt(int32 cpuIntLine)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
//Check if interrupts are enabled here because EIE bit isn't checked by CMIPS
|
|
|
|
if((m_ee.m_State.nCOP0[CCOP_SCU::STATUS] & INTERRUPTS_ENABLED_MASK) != INTERRUPTS_ENABLED_MASK)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-15 10:43:42 -04:00
|
|
|
if(!m_ee.CanGenerateInterrupt())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-09 11:26:48 -05:00
|
|
|
if(m_currentThreadId != m_idleThreadId)
|
|
|
|
{
|
|
|
|
auto thread = m_threads[m_currentThreadId];
|
|
|
|
ThreadSaveContext(thread, true);
|
2022-01-02 15:38:36 -05:00
|
|
|
|
|
|
|
m_idleEvaluator.NotifyEvent(Ee::CIdleEvaluator::EVENT_INTERRUPT, 0);
|
2019-02-09 11:26:48 -05:00
|
|
|
}
|
|
|
|
|
2021-12-03 13:44:24 -05:00
|
|
|
//Update CAUSE register
|
|
|
|
m_ee.m_State.nCOP0[CCOP_SCU::CAUSE] &= ~(CCOP_SCU::CAUSE_EXCCODE_MASK | CCOP_SCU::CAUSE_IP_2 | CCOP_SCU::CAUSE_IP_3);
|
|
|
|
m_ee.m_State.nCOP0[CCOP_SCU::CAUSE] |= CCOP_SCU::CAUSE_EXCCODE_INT;
|
|
|
|
|
|
|
|
switch(cpuIntLine)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
//INTC interrupt
|
|
|
|
m_ee.m_State.nCOP0[CCOP_SCU::CAUSE] |= CCOP_SCU::CAUSE_IP_2;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
//DMAC interrupt
|
|
|
|
m_ee.m_State.nCOP0[CCOP_SCU::CAUSE] |= CCOP_SCU::CAUSE_IP_3;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-05-02 08:56:25 -04:00
|
|
|
FRAMEWORK_MAYBE_UNUSED bool interrupted = m_ee.GenerateInterrupt(BIOS_ADDRESS_INTERRUPTHANDLER);
|
2017-06-15 10:43:42 -04:00
|
|
|
assert(interrupted);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::HandleReturnFromException()
|
|
|
|
{
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
|
|
|
|
2015-05-17 18:58:19 -04:00
|
|
|
bool CPS2OS::CheckVBlankFlag()
|
2015-05-15 19:14:03 -04:00
|
|
|
{
|
2015-05-17 18:58:19 -04:00
|
|
|
bool changed = false;
|
2015-05-15 19:14:03 -04:00
|
|
|
auto vsyncFlagPtrs = GetVsyncFlagPtrs();
|
|
|
|
|
|
|
|
if(vsyncFlagPtrs.first != 0)
|
|
|
|
{
|
2020-04-16 09:04:13 -04:00
|
|
|
auto vsyncFlagFirst = GetStructPtr(vsyncFlagPtrs.first);
|
|
|
|
*reinterpret_cast<uint32*>(vsyncFlagFirst) = 1;
|
2015-05-17 18:58:19 -04:00
|
|
|
changed = true;
|
2015-05-15 19:14:03 -04:00
|
|
|
}
|
|
|
|
if(vsyncFlagPtrs.second != 0)
|
|
|
|
{
|
2020-04-16 09:04:13 -04:00
|
|
|
auto vsyncFlagSecond = GetStructPtr(vsyncFlagPtrs.second);
|
|
|
|
*reinterpret_cast<uint64*>(vsyncFlagSecond) = m_gs->ReadPrivRegister(CGSHandler::GS_CSR);
|
2015-05-17 18:58:19 -04:00
|
|
|
changed = true;
|
2015-05-15 19:14:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
SetVsyncFlagPtrs(0, 0);
|
2015-05-17 18:58:19 -04:00
|
|
|
return changed;
|
2015-05-15 19:14:03 -04:00
|
|
|
}
|
|
|
|
|
2021-11-08 14:11:06 -05:00
|
|
|
uint32 CPS2OS::SuspendCurrentThread()
|
|
|
|
{
|
|
|
|
//For HLE module usage
|
|
|
|
|
|
|
|
uint32 threadId = m_currentThreadId;
|
|
|
|
|
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
assert(thread);
|
|
|
|
assert(thread->status == THREAD_RUNNING);
|
|
|
|
|
|
|
|
thread->status = THREAD_SUSPENDED;
|
|
|
|
UnlinkThread(threadId);
|
|
|
|
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
|
|
|
|
return threadId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2OS::ResumeThread(uint32 threadId)
|
|
|
|
{
|
|
|
|
//For HLE module usage
|
|
|
|
|
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
assert(thread);
|
|
|
|
assert(thread->status == THREAD_SUSPENDED);
|
|
|
|
|
|
|
|
thread->status = THREAD_RUNNING;
|
|
|
|
LinkThread(threadId);
|
|
|
|
}
|
|
|
|
|
2020-08-04 13:29:59 -04:00
|
|
|
void CPS2OS::UpdateTLBEnabledState()
|
|
|
|
{
|
|
|
|
bool TLBenabled = (m_tlblExceptionHandler != 0) || (m_tlbsExceptionHandler != 0);
|
|
|
|
if(TLBenabled)
|
|
|
|
{
|
|
|
|
m_ee.m_pAddrTranslator = &TranslateAddressTLB;
|
|
|
|
m_ee.m_TLBExceptionChecker = &CheckTLBExceptions;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_ee.m_pAddrTranslator = &TranslateAddress;
|
|
|
|
m_ee.m_TLBExceptionChecker = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//The EE kernel defines some default TLB entries
|
|
|
|
//These defines seek to allow simple translation without scanning the TLB
|
|
|
|
|
|
|
|
//Direct access to physical addresses
|
|
|
|
#define TLB_IS_DIRECT_VADDRESS(a) ((a) <= 0x1FFFFFFF)
|
|
|
|
#define TLB_TRANSLATE_DIRECT_VADDRESS(a) ((a)&0x1FFFFFFF)
|
|
|
|
|
|
|
|
//RAM access, "uncached" as per TLB entry
|
|
|
|
#define TLB_IS_UNCACHED_RAM_VADDRESS(a) (((a) >= 0x20100000) && ((a) <= 0x21FFFFFF))
|
|
|
|
#define TLB_TRANSLATE_UNCACHED_RAM_VADDRESS(a) ((a)-0x20000000)
|
|
|
|
|
|
|
|
//RAM access, "uncached & accelerated" as per TLB entry
|
|
|
|
#define TLB_IS_UNCACHED_FAST_RAM_VADDRESS(a) (((a) >= 0x30100000) && ((a) <= 0x31FFFFFF))
|
|
|
|
#define TLB_TRANSLATE_UNCACHED_FAST_RAM_VADDRESS(a) ((a)-0x30000000)
|
|
|
|
|
|
|
|
//SPR memory access, the TLB entry for this should have the SPR bit set
|
|
|
|
#define TLB_IS_SPR_VADDRESS(a) (((a) >= 0x70000000) && ((a) <= 0x70003FFF))
|
|
|
|
#define TLB_TRANSLATE_SPR_VADDRESS(a) ((a) - (0x70000000 - PS2::EE_SPR_ADDR))
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
uint32 CPS2OS::TranslateAddress(CMIPS*, uint32 vaddrLo)
|
|
|
|
{
|
2020-08-04 13:29:59 -04:00
|
|
|
if(TLB_IS_SPR_VADDRESS(vaddrLo))
|
|
|
|
{
|
|
|
|
return TLB_TRANSLATE_SPR_VADDRESS(vaddrLo);
|
|
|
|
}
|
|
|
|
if(TLB_IS_UNCACHED_FAST_RAM_VADDRESS(vaddrLo))
|
|
|
|
{
|
|
|
|
return TLB_TRANSLATE_UNCACHED_FAST_RAM_VADDRESS(vaddrLo);
|
|
|
|
}
|
|
|
|
//No need to check "uncached" RAM access here, will be translated correctly by the macro below
|
|
|
|
return TLB_TRANSLATE_DIRECT_VADDRESS(vaddrLo);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CPS2OS::TranslateAddressTLB(CMIPS* context, uint32 vaddrLo)
|
|
|
|
{
|
|
|
|
if(TLB_IS_DIRECT_VADDRESS(vaddrLo))
|
|
|
|
{
|
|
|
|
return TLB_TRANSLATE_DIRECT_VADDRESS(vaddrLo);
|
|
|
|
}
|
|
|
|
if(TLB_IS_UNCACHED_RAM_VADDRESS(vaddrLo))
|
|
|
|
{
|
|
|
|
return TLB_TRANSLATE_UNCACHED_RAM_VADDRESS(vaddrLo);
|
|
|
|
}
|
|
|
|
if(TLB_IS_UNCACHED_FAST_RAM_VADDRESS(vaddrLo))
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2020-08-04 13:29:59 -04:00
|
|
|
return TLB_TRANSLATE_UNCACHED_FAST_RAM_VADDRESS(vaddrLo);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
2020-08-04 13:29:59 -04:00
|
|
|
if(TLB_IS_SPR_VADDRESS(vaddrLo))
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2020-08-04 13:29:59 -04:00
|
|
|
return TLB_TRANSLATE_SPR_VADDRESS(vaddrLo);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
2020-08-04 13:29:59 -04:00
|
|
|
for(uint32 i = 0; i < MIPSSTATE::TLB_ENTRY_MAX; i++)
|
|
|
|
{
|
|
|
|
const auto& entry = context->m_State.tlbEntries[i];
|
|
|
|
if(entry.entryHi == 0) continue;
|
|
|
|
|
|
|
|
uint32 pageSize = ((entry.pageMask >> 13) + 1) * 0x1000;
|
|
|
|
uint32 vpnMask = (pageSize * 2) - 1;
|
|
|
|
uint32 vpn = vaddrLo & ~vpnMask;
|
|
|
|
uint32 tlbVpn = entry.entryHi & ~vpnMask;
|
|
|
|
|
|
|
|
if(vpn == tlbVpn)
|
|
|
|
{
|
|
|
|
uint32 mask = (pageSize - 1);
|
|
|
|
uint32 entryLo = (vaddrLo & pageSize) ? entry.entryLo1 : entry.entryLo0;
|
|
|
|
assert(entryLo & CCOP_SCU::ENTRYLO_GLOBAL);
|
|
|
|
assert(entryLo & CCOP_SCU::ENTRYLO_VALID);
|
|
|
|
uint32 pfn = ((entryLo >> 6) << 12);
|
|
|
|
uint32 paddr = pfn + (vaddrLo & mask);
|
|
|
|
return paddr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Shouldn't come here
|
|
|
|
//assert(false);
|
2015-05-06 00:54:15 -04:00
|
|
|
return vaddrLo & 0x1FFFFFFF;
|
|
|
|
}
|
|
|
|
|
2020-08-04 13:29:59 -04:00
|
|
|
uint32 CPS2OS::CheckTLBExceptions(CMIPS* context, uint32 vaddrLo, uint32 isWrite)
|
|
|
|
{
|
|
|
|
if(TLB_IS_DIRECT_VADDRESS(vaddrLo))
|
|
|
|
{
|
|
|
|
return MIPS_EXCEPTION_NONE;
|
|
|
|
}
|
|
|
|
if(TLB_IS_UNCACHED_RAM_VADDRESS(vaddrLo))
|
|
|
|
{
|
|
|
|
return MIPS_EXCEPTION_NONE;
|
|
|
|
}
|
|
|
|
if(TLB_IS_UNCACHED_FAST_RAM_VADDRESS(vaddrLo))
|
|
|
|
{
|
|
|
|
return MIPS_EXCEPTION_NONE;
|
|
|
|
}
|
|
|
|
if(TLB_IS_SPR_VADDRESS(vaddrLo))
|
|
|
|
{
|
|
|
|
return MIPS_EXCEPTION_NONE;
|
|
|
|
}
|
|
|
|
for(uint32 i = 0; i < MIPSSTATE::TLB_ENTRY_MAX; i++)
|
|
|
|
{
|
|
|
|
const auto& entry = context->m_State.tlbEntries[i];
|
|
|
|
if(entry.entryHi == 0) continue;
|
|
|
|
|
|
|
|
uint32 pageSize = ((entry.pageMask >> 13) + 1) * 0x1000;
|
|
|
|
uint32 vpnMask = (pageSize * 2) - 1;
|
|
|
|
uint32 vpn = vaddrLo & ~vpnMask;
|
|
|
|
uint32 tlbVpn = entry.entryHi & ~vpnMask;
|
|
|
|
|
|
|
|
if(vpn == tlbVpn)
|
|
|
|
{
|
|
|
|
uint32 entryLo = (vaddrLo & pageSize) ? entry.entryLo1 : entry.entryLo0;
|
|
|
|
assert(entryLo & CCOP_SCU::ENTRYLO_GLOBAL);
|
|
|
|
if((entryLo & CCOP_SCU::ENTRYLO_VALID) == 0)
|
|
|
|
{
|
|
|
|
//This causes a TLBL or TLBS exception, but should use the common vector
|
|
|
|
context->m_State.nCOP0[CCOP_SCU::CAUSE] &= ~CCOP_SCU::CAUSE_EXCCODE_MASK;
|
|
|
|
context->m_State.nCOP0[CCOP_SCU::CAUSE] |= isWrite ? CCOP_SCU::CAUSE_EXCCODE_TLBS : CCOP_SCU::CAUSE_EXCCODE_TLBL;
|
|
|
|
context->m_State.nCOP0[CCOP_SCU::BADVADDR] = vaddrLo;
|
|
|
|
context->m_State.nHasException = MIPS_EXCEPTION_TLB;
|
|
|
|
return context->m_State.nHasException;
|
|
|
|
}
|
|
|
|
assert(!isWrite || ((entryLo & CCOP_SCU::ENTRYLO_DIRTY) != 0));
|
|
|
|
return MIPS_EXCEPTION_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//assert(false);
|
|
|
|
//We should probably raise some kind of exception here since we didn't match anything
|
|
|
|
return MIPS_EXCEPTION_NONE;
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//////////////////////////////////////////////////
|
|
|
|
//System Calls
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void CPS2OS::sc_Unhandled()
|
|
|
|
{
|
2018-05-25 12:38:59 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Unknown system call (0x%X) called from 0x%08X.\r\n", m_ee.m_State.nGPR[3].nV[0], m_ee.m_State.nPC);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//02
|
|
|
|
void CPS2OS::sc_GsSetCrt()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
bool isInterlaced = (m_ee.m_State.nGPR[SC_PARAM0].nV[0] != 0);
|
|
|
|
unsigned int mode = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
bool isFrameMode = (m_ee.m_State.nGPR[SC_PARAM2].nV[0] != 0);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
if(m_gs != NULL)
|
|
|
|
{
|
|
|
|
m_gs->SetCrt(isInterlaced, mode, isFrameMode);
|
|
|
|
}
|
2021-02-24 16:04:10 -05:00
|
|
|
|
|
|
|
OnCrtModeChange();
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//04
|
|
|
|
void CPS2OS::sc_Exit()
|
|
|
|
{
|
|
|
|
OnRequestExit();
|
|
|
|
}
|
|
|
|
|
|
|
|
//06
|
|
|
|
void CPS2OS::sc_LoadExecPS2()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 filePathPtr = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 argCount = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
uint32 argValuesPtr = m_ee.m_State.nGPR[SC_PARAM2].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
ArgumentList arguments;
|
|
|
|
for(uint32 i = 0; i < argCount; i++)
|
|
|
|
{
|
2015-12-02 22:40:54 -05:00
|
|
|
uint32 argValuePtr = *reinterpret_cast<uint32*>(GetStructPtr(argValuesPtr + i * 4));
|
|
|
|
arguments.push_back(reinterpret_cast<const char*>(GetStructPtr(argValuePtr)));
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2015-12-02 22:40:54 -05:00
|
|
|
std::string filePath = reinterpret_cast<const char*>(GetStructPtr(filePathPtr));
|
|
|
|
if(filePath.find(':') == std::string::npos)
|
|
|
|
{
|
|
|
|
//Unreal Tournament doesn't provide drive info in path
|
|
|
|
filePath = "cdrom0:" + filePath;
|
|
|
|
}
|
|
|
|
OnRequestLoadExecutable(filePath.c_str(), arguments);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2015-06-20 02:15:55 -04:00
|
|
|
//07
|
|
|
|
void CPS2OS::sc_ExecPS2()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 pc = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 gp = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
uint32 argCount = m_ee.m_State.nGPR[SC_PARAM2].nV[0];
|
|
|
|
uint32 argValuesPtr = m_ee.m_State.nGPR[SC_PARAM3].nV[0];
|
2015-06-20 02:15:55 -04:00
|
|
|
|
2016-05-08 17:43:02 -04:00
|
|
|
//This is used by Gran Turismo 4. The game clears the program
|
|
|
|
//loaded initially, so, we need to make sure there's no stale
|
|
|
|
//threads or handlers
|
|
|
|
|
|
|
|
sc_ExitDeleteThread();
|
|
|
|
assert(m_currentThreadId == m_idleThreadId);
|
|
|
|
|
|
|
|
//Clear all remaining INTC handlers
|
|
|
|
std::vector<uint32> intcHandlerIds;
|
|
|
|
for(const auto& intcHandlerPair : m_intcHandlerQueue)
|
|
|
|
{
|
|
|
|
intcHandlerIds.push_back(intcHandlerPair.first);
|
|
|
|
}
|
|
|
|
for(const auto& intcHandlerId : intcHandlerIds)
|
|
|
|
{
|
|
|
|
m_intcHandlerQueue.Unlink(intcHandlerId);
|
|
|
|
m_intcHandlers.Free(intcHandlerId);
|
|
|
|
}
|
2018-09-14 13:28:23 -04:00
|
|
|
//Also make sure all interrupts are masked
|
|
|
|
{
|
|
|
|
uint32 intcMask = m_ee.m_pMemoryMap->GetWord(CINTC::INTC_MASK);
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CINTC::INTC_MASK, intcMask);
|
|
|
|
}
|
2016-05-08 17:43:02 -04:00
|
|
|
|
2015-06-20 02:15:55 -04:00
|
|
|
m_ee.m_State.nPC = pc;
|
|
|
|
m_ee.m_State.nGPR[CMIPS::GP].nD0 = static_cast<int32>(gp);
|
2018-10-11 12:41:37 -04:00
|
|
|
|
|
|
|
ArgumentList arguments;
|
|
|
|
for(uint32 i = 0; i < argCount; i++)
|
|
|
|
{
|
|
|
|
uint32 argValuePtr = *reinterpret_cast<uint32*>(GetStructPtr(argValuesPtr + i * 4));
|
|
|
|
arguments.push_back(reinterpret_cast<const char*>(GetStructPtr(argValuePtr)));
|
|
|
|
}
|
|
|
|
m_currentArguments = arguments;
|
2015-06-20 02:15:55 -04:00
|
|
|
}
|
|
|
|
|
2018-08-27 07:31:18 -04:00
|
|
|
//0D
|
|
|
|
void CPS2OS::sc_SetVTLBRefillHandler()
|
|
|
|
{
|
2019-02-21 18:51:00 -05:00
|
|
|
uint32 cause = m_ee.m_State.nGPR[SC_PARAM0].nV0;
|
|
|
|
uint32 handler = m_ee.m_State.nGPR[SC_PARAM1].nV0;
|
|
|
|
|
2020-08-04 13:29:59 -04:00
|
|
|
switch(cause << 2)
|
2019-02-21 18:51:00 -05:00
|
|
|
{
|
2020-08-04 13:29:59 -04:00
|
|
|
case CCOP_SCU::CAUSE_EXCCODE_TLBL:
|
2019-02-21 18:51:00 -05:00
|
|
|
m_tlblExceptionHandler = handler;
|
|
|
|
break;
|
2020-08-04 13:29:59 -04:00
|
|
|
case CCOP_SCU::CAUSE_EXCCODE_TLBS:
|
2019-02-21 18:51:00 -05:00
|
|
|
m_tlbsExceptionHandler = handler;
|
|
|
|
break;
|
2022-03-23 15:43:29 -04:00
|
|
|
default:
|
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Setting handler 0x%08X for unknown exception %d.\r\n", handler, cause);
|
|
|
|
break;
|
2019-02-21 18:51:00 -05:00
|
|
|
}
|
2020-08-04 13:29:59 -04:00
|
|
|
|
|
|
|
UpdateTLBEnabledState();
|
2022-03-23 15:43:29 -04:00
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(handler);
|
2018-08-27 07:31:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//0E
|
|
|
|
void CPS2OS::sc_SetVCommonHandler()
|
|
|
|
{
|
2022-03-23 15:43:29 -04:00
|
|
|
uint32 cause = m_ee.m_State.nGPR[SC_PARAM0].nV0;
|
|
|
|
uint32 handler = m_ee.m_State.nGPR[SC_PARAM1].nV0;
|
|
|
|
|
|
|
|
switch(cause << 2)
|
|
|
|
{
|
|
|
|
case CCOP_SCU::CAUSE_EXCCODE_TRAP:
|
|
|
|
m_trapExceptionHandler = handler;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Setting handler 0x%08X for unknown exception %d.\r\n", handler, cause);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(handler);
|
2018-08-27 07:31:18 -04:00
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//10
|
|
|
|
void CPS2OS::sc_AddIntcHandler()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 cause = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 address = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
uint32 next = m_ee.m_State.nGPR[SC_PARAM2].nV[0];
|
|
|
|
uint32 arg = m_ee.m_State.nGPR[SC_PARAM3].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
uint32 id = m_intcHandlers.Allocate();
|
|
|
|
if(static_cast<int32>(id) == -1)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-07-25 20:13:59 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
auto handler = m_intcHandlers[id];
|
2018-04-30 21:01:23 +01:00
|
|
|
handler->address = address;
|
|
|
|
handler->cause = cause;
|
|
|
|
handler->arg = arg;
|
|
|
|
handler->gp = m_ee.m_State.nGPR[CMIPS::GP].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
if(next == 0)
|
|
|
|
{
|
|
|
|
m_intcHandlerQueue.PushFront(id);
|
|
|
|
}
|
|
|
|
else if(static_cast<int32>(next) == -1)
|
|
|
|
{
|
|
|
|
m_intcHandlerQueue.PushBack(id);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_intcHandlerQueue.AddBefore(next, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//11
|
|
|
|
void CPS2OS::sc_RemoveIntcHandler()
|
|
|
|
{
|
2017-10-04 07:20:42 -04:00
|
|
|
uint32 cause = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
auto handler = m_intcHandlers[id];
|
|
|
|
if(!handler)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-07-25 20:13:59 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
2017-09-28 09:43:06 -04:00
|
|
|
assert(handler->cause == cause);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
m_intcHandlerQueue.Unlink(id);
|
|
|
|
m_intcHandlers.Free(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2017-10-04 07:25:13 -04:00
|
|
|
int32 handlerCount = 0;
|
|
|
|
for(const auto& handler : m_intcHandlers)
|
|
|
|
{
|
|
|
|
if(!handler) continue;
|
|
|
|
if(handler->cause != cause) continue;
|
|
|
|
handlerCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = handlerCount;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//12
|
|
|
|
void CPS2OS::sc_AddDmacHandler()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 channel = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 address = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
uint32 next = m_ee.m_State.nGPR[SC_PARAM2].nV[0];
|
|
|
|
uint32 arg = m_ee.m_State.nGPR[SC_PARAM3].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
uint32 id = m_dmacHandlers.Allocate();
|
2015-07-26 01:11:43 -04:00
|
|
|
if(static_cast<int32>(id) == -1)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-07-26 01:11:43 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto handler = m_dmacHandlers[id];
|
2018-04-30 21:01:23 +01:00
|
|
|
handler->address = address;
|
|
|
|
handler->channel = channel;
|
|
|
|
handler->arg = arg;
|
|
|
|
handler->gp = m_ee.m_State.nGPR[CMIPS::GP].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-26 01:11:43 -04:00
|
|
|
if(next == 0)
|
|
|
|
{
|
|
|
|
m_dmacHandlerQueue.PushFront(id);
|
|
|
|
}
|
|
|
|
else if(static_cast<int32>(next) == -1)
|
|
|
|
{
|
|
|
|
m_dmacHandlerQueue.PushBack(id);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_dmacHandlerQueue.AddBefore(next, id);
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
//13
|
|
|
|
void CPS2OS::sc_RemoveDmacHandler()
|
|
|
|
{
|
2017-10-04 07:20:42 -04:00
|
|
|
uint32 channel = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
auto handler = m_dmacHandlers[id];
|
2015-07-26 01:11:43 -04:00
|
|
|
if(!handler)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-07-26 01:11:43 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
2017-09-28 09:43:06 -04:00
|
|
|
assert(handler->channel == channel);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-07-26 01:11:43 -04:00
|
|
|
m_dmacHandlerQueue.Unlink(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
m_dmacHandlers.Free(id);
|
|
|
|
|
2017-10-04 07:25:13 -04:00
|
|
|
int32 handlerCount = 0;
|
|
|
|
for(const auto& handler : m_dmacHandlers)
|
|
|
|
{
|
|
|
|
if(!handler) continue;
|
|
|
|
if(handler->channel != channel) continue;
|
|
|
|
handlerCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = handlerCount;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//14
|
2016-09-25 14:53:30 -04:00
|
|
|
//1A
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::sc_EnableIntc()
|
|
|
|
{
|
|
|
|
uint32 cause = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 mask = 1 << cause;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
if(!(m_ee.m_pMemoryMap->GetWord(CINTC::INTC_MASK) & mask))
|
|
|
|
{
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CINTC::INTC_MASK, mask);
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = changed ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//15
|
2016-09-25 14:53:30 -04:00
|
|
|
//1B
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::sc_DisableIntc()
|
|
|
|
{
|
|
|
|
uint32 cause = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 mask = 1 << cause;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
if(m_ee.m_pMemoryMap->GetWord(CINTC::INTC_MASK) & mask)
|
|
|
|
{
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CINTC::INTC_MASK, mask);
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = changed ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//16
|
2016-08-29 22:15:29 +09:00
|
|
|
//1C
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::sc_EnableDmac()
|
|
|
|
{
|
|
|
|
uint32 channel = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 registerId = 0x10000 << channel;
|
2021-12-03 13:46:31 -05:00
|
|
|
bool changed = false;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
if(!(m_ee.m_pMemoryMap->GetWord(CDMAC::D_STAT) & registerId))
|
|
|
|
{
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CDMAC::D_STAT, registerId);
|
2021-12-03 13:46:31 -05:00
|
|
|
changed = true;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2021-12-03 13:46:31 -05:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = changed ? 1 : 0;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//17
|
2016-08-29 22:15:29 +09:00
|
|
|
//1D
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::sc_DisableDmac()
|
|
|
|
{
|
|
|
|
uint32 channel = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 registerId = 0x10000 << channel;
|
2021-12-03 13:46:31 -05:00
|
|
|
bool changed = false;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
if(m_ee.m_pMemoryMap->GetWord(CDMAC::D_STAT) & registerId)
|
|
|
|
{
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CDMAC::D_STAT, registerId);
|
2021-12-03 13:46:31 -05:00
|
|
|
changed = true;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
2021-12-03 13:46:31 -05:00
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = changed ? 1 : 0;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2019-08-16 07:49:13 -04:00
|
|
|
//18/1E
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::sc_SetAlarm()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 delay = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 callback = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
uint32 callbackParam = m_ee.m_State.nGPR[SC_PARAM2].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
auto alarmId = m_alarms.Allocate();
|
|
|
|
assert(alarmId != -1);
|
|
|
|
if(alarmId == -1)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-01 16:56:37 -04:00
|
|
|
//Check limits (0 delay will probably be problematic and delay is a short)
|
|
|
|
assert((delay > 0) && (delay < 0x10000));
|
2021-10-01 11:03:14 -04:00
|
|
|
|
|
|
|
uint32 currentCount = m_ee.m_pMemoryMap->GetWord(CTimer::T3_COUNT);
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
auto alarm = m_alarms[alarmId];
|
2018-04-30 21:01:23 +01:00
|
|
|
alarm->delay = delay;
|
2021-10-01 11:03:14 -04:00
|
|
|
alarm->compare = currentCount + delay;
|
2018-04-30 21:01:23 +01:00
|
|
|
alarm->callback = callback;
|
|
|
|
alarm->callbackParam = callbackParam;
|
|
|
|
alarm->gp = m_ee.m_State.nGPR[CMIPS::GP].nV0;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-10-01 11:03:14 -04:00
|
|
|
AlarmUpdateCompare();
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = alarmId;
|
|
|
|
}
|
|
|
|
|
|
|
|
//1F
|
|
|
|
void CPS2OS::sc_ReleaseAlarm()
|
|
|
|
{
|
|
|
|
uint32 alarmId = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
|
|
|
|
auto alarm = m_alarms[alarmId];
|
|
|
|
if(alarm == nullptr)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_alarms.Free(alarmId);
|
2021-10-01 11:03:14 -04:00
|
|
|
|
|
|
|
AlarmUpdateCompare();
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//20
|
|
|
|
void CPS2OS::sc_CreateThread()
|
|
|
|
{
|
|
|
|
auto threadParam = reinterpret_cast<THREADPARAM*>(GetStructPtr(m_ee.m_State.nGPR[SC_PARAM0].nV0));
|
|
|
|
|
2023-11-16 17:58:37 -05:00
|
|
|
uint32 id = m_threads.AllocateAt(m_state->allocateThreadNextId);
|
2015-08-06 01:20:04 -04:00
|
|
|
if(static_cast<int32>(id) == -1)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-06 01:20:04 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-08 23:36:00 -04:00
|
|
|
auto parentThread = m_threads[m_currentThreadId];
|
2015-08-06 01:20:04 -04:00
|
|
|
assert(parentThread);
|
2015-05-06 00:54:15 -04:00
|
|
|
uint32 heapBase = parentThread->heapBase;
|
|
|
|
|
2015-07-12 00:00:35 -04:00
|
|
|
assert(threadParam->initPriority < 128);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->status = THREAD_ZOMBIE;
|
|
|
|
thread->stackBase = threadParam->stackBase;
|
|
|
|
thread->epc = threadParam->threadProc;
|
|
|
|
thread->threadProc = threadParam->threadProc;
|
|
|
|
thread->initPriority = threadParam->initPriority;
|
|
|
|
thread->heapBase = heapBase;
|
|
|
|
thread->wakeUpCount = 0;
|
|
|
|
thread->gp = threadParam->gp;
|
|
|
|
thread->stackSize = threadParam->stackSize;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2016-11-20 17:09:58 -05:00
|
|
|
ThreadReset(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2016-11-20 15:36:48 -05:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//21
|
|
|
|
void CPS2OS::sc_DeleteThread()
|
|
|
|
{
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
|
2015-08-08 23:36:00 -04:00
|
|
|
if(id == m_currentThreadId)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-05-28 00:54:05 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-06 13:18:22 -04:00
|
|
|
//Prevent deletion of idle thread.
|
|
|
|
//Taiko no Tatsujin 14 tries to delete all threads (including this one) before starting a new EE exec
|
|
|
|
if(id == m_idleThreadId)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-28 00:54:05 -04:00
|
|
|
if(id >= MAX_THREAD)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-05-28 00:54:05 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(thread->status != THREAD_ZOMBIE)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
m_threads.Free(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-05-28 00:54:05 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//22
|
|
|
|
void CPS2OS::sc_StartThread()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 arg = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-06 01:20:04 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(thread->status == THREAD_ZOMBIE);
|
|
|
|
thread->status = THREAD_RUNNING;
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->epc = thread->threadProc;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2016-12-24 18:25:56 -05:00
|
|
|
auto context = reinterpret_cast<THREADCONTEXT*>(GetStructPtr(thread->contextPtr));
|
2015-05-06 00:54:15 -04:00
|
|
|
context->gpr[CMIPS::A0].nV0 = arg;
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
2015-05-24 23:02:55 -04:00
|
|
|
|
2015-08-09 00:03:42 -04:00
|
|
|
LinkThread(id);
|
2015-05-24 23:02:55 -04:00
|
|
|
ThreadShakeAndBake();
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//23
|
|
|
|
void CPS2OS::sc_ExitThread()
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
uint32 threadId = m_currentThreadId;
|
2015-07-12 00:01:51 -04:00
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[threadId];
|
2015-05-06 00:54:15 -04:00
|
|
|
thread->status = THREAD_ZOMBIE;
|
2015-08-09 00:03:42 -04:00
|
|
|
UnlinkThread(threadId);
|
2015-07-12 00:01:51 -04:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
ThreadShakeAndBake();
|
2016-11-20 17:09:58 -05:00
|
|
|
ThreadReset(threadId);
|
2015-10-27 00:22:51 -04:00
|
|
|
|
|
|
|
CheckLivingThreads();
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2015-06-03 04:05:59 -04:00
|
|
|
//24
|
|
|
|
void CPS2OS::sc_ExitDeleteThread()
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
uint32 threadId = m_currentThreadId;
|
2015-08-06 01:20:04 -04:00
|
|
|
|
|
|
|
auto thread = m_threads[threadId];
|
2015-06-03 04:05:59 -04:00
|
|
|
thread->status = THREAD_ZOMBIE;
|
2015-08-09 00:03:42 -04:00
|
|
|
UnlinkThread(threadId);
|
|
|
|
|
2015-06-03 04:05:59 -04:00
|
|
|
ThreadShakeAndBake();
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
m_threads.Free(threadId);
|
2015-10-27 00:22:51 -04:00
|
|
|
|
|
|
|
CheckLivingThreads();
|
2015-06-03 04:05:59 -04:00
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//25
|
|
|
|
void CPS2OS::sc_TerminateThread()
|
|
|
|
{
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
|
2015-08-08 23:36:00 -04:00
|
|
|
if(id == m_currentThreadId)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-07-04 22:35:53 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-07-04 22:35:53 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(thread->status == THREAD_ZOMBIE)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-23 09:14:49 -04:00
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_RUNNING:
|
2021-07-23 09:24:38 -04:00
|
|
|
UnlinkThread(id);
|
2021-07-23 09:14:49 -04:00
|
|
|
break;
|
|
|
|
case THREAD_WAITING:
|
|
|
|
case THREAD_SUSPENDED_WAITING:
|
|
|
|
SemaUnlinkThread(thread->semaWait, id);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
[[fallthrough]];
|
|
|
|
case THREAD_SLEEPING:
|
|
|
|
case THREAD_SUSPENDED:
|
|
|
|
case THREAD_SUSPENDED_SLEEPING:
|
|
|
|
//Nothing to do
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
thread->status = THREAD_ZOMBIE;
|
2016-11-20 17:09:58 -05:00
|
|
|
ThreadReset(id);
|
2015-07-12 00:01:51 -04:00
|
|
|
|
2015-07-04 22:35:53 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//29
|
|
|
|
//2A
|
|
|
|
void CPS2OS::sc_ChangeThreadPriority()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
bool isInt = m_ee.m_State.nGPR[3].nV[0] == 0x2A;
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 prio = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-06 01:20:04 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-12 00:00:35 -04:00
|
|
|
uint32 prevPrio = thread->currPriority;
|
|
|
|
thread->currPriority = prio;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(prevPrio);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-09 00:03:42 -04:00
|
|
|
//Reschedule if thread is already running
|
|
|
|
if(thread->status == THREAD_RUNNING)
|
|
|
|
{
|
|
|
|
UnlinkThread(id);
|
|
|
|
LinkThread(id);
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
if(!isInt)
|
|
|
|
{
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//2B
|
|
|
|
void CPS2OS::sc_RotateThreadReadyQueue()
|
|
|
|
{
|
|
|
|
uint32 prio = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
|
2021-11-09 09:30:16 -05:00
|
|
|
uint32 threadIdBefore = m_currentThreadId;
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Find first of this priority and reinsert if it's the same as the current thread
|
|
|
|
//If it's not the same, the schedule will be rotated when another thread is choosen
|
2015-08-09 00:03:42 -04:00
|
|
|
for(auto threadSchedulePair : m_threadSchedule)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-09 00:03:42 -04:00
|
|
|
auto scheduledThread = threadSchedulePair.second;
|
|
|
|
if(scheduledThread->currPriority == prio)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-09 00:03:42 -04:00
|
|
|
UnlinkThread(threadSchedulePair.first);
|
|
|
|
LinkThread(threadSchedulePair.first);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-09 00:03:42 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(prio);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-09 00:03:42 -04:00
|
|
|
ThreadShakeAndBake();
|
2021-11-09 09:30:16 -05:00
|
|
|
|
2021-12-09 15:22:19 -05:00
|
|
|
m_idleEvaluator.NotifyEvent(Ee::CIdleEvaluator::EVENT_ROTATETHREADREADYQUEUE, threadIdBefore, m_currentThreadId);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2018-06-19 19:12:34 -04:00
|
|
|
//2D/2E
|
|
|
|
void CPS2OS::sc_ReleaseWaitThread()
|
|
|
|
{
|
2018-06-24 22:27:58 -04:00
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2018-06-19 19:12:34 -04:00
|
|
|
bool isInt = m_ee.m_State.nGPR[3].nV[0] == 0x2E;
|
|
|
|
|
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
|
|
|
{
|
2022-07-19 09:42:51 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, SYSCALL_NAME_RELEASEWAITTHREAD ": Used on thread that doesn't exist (%d).\r\n",
|
2018-06-24 22:27:58 -04:00
|
|
|
id);
|
2018-06-19 19:12:34 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Must also work with THREAD_WAITING
|
|
|
|
assert(thread->status != THREAD_WAITING);
|
|
|
|
if(thread->status != THREAD_SLEEPING)
|
|
|
|
{
|
2022-07-19 09:42:51 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, SYSCALL_NAME_RELEASEWAITTHREAD ": Used on non sleeping thread (%d).\r\n",
|
2018-06-24 22:27:58 -04:00
|
|
|
id);
|
2018-06-19 19:12:34 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: Check what happens to wakeUpCount
|
|
|
|
thread->wakeUpCount = 0;
|
|
|
|
thread->status = THREAD_RUNNING;
|
|
|
|
LinkThread(id);
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
|
|
|
|
|
|
|
if(!isInt)
|
|
|
|
{
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//2F
|
|
|
|
void CPS2OS::sc_GetThreadId()
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[0] = m_currentThreadId;
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//30
|
|
|
|
void CPS2OS::sc_ReferThreadStatus()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 statusPtr = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-05-19 03:18:31 -04:00
|
|
|
if(id >= MAX_THREAD)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(id == 0)
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
id = m_currentThreadId;
|
2015-05-19 03:18:31 -04:00
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2021-03-17 13:59:59 -04:00
|
|
|
//Returns 0 if thread has been deleted (needed by MGS2)
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(0);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 ret = 0;
|
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_RUNNING:
|
2015-08-08 23:36:00 -04:00
|
|
|
if(id == m_currentThreadId)
|
2015-05-22 01:26:38 -04:00
|
|
|
{
|
|
|
|
ret = THS_RUN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = THS_READY;
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case THREAD_WAITING:
|
|
|
|
case THREAD_SLEEPING:
|
2015-05-22 01:26:38 -04:00
|
|
|
ret = THS_WAIT;
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED:
|
2015-05-22 01:26:38 -04:00
|
|
|
ret = THS_SUSPEND;
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED_WAITING:
|
|
|
|
case THREAD_SUSPENDED_SLEEPING:
|
2015-05-22 01:26:38 -04:00
|
|
|
ret = (THS_WAIT | THS_SUSPEND);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case THREAD_ZOMBIE:
|
2015-05-22 01:26:38 -04:00
|
|
|
ret = THS_DORMANT;
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-01-10 22:00:18 -05:00
|
|
|
uint32 waitType = 0;
|
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_SLEEPING:
|
|
|
|
case THREAD_SUSPENDED_SLEEPING:
|
|
|
|
waitType = 1;
|
|
|
|
break;
|
|
|
|
case THREAD_WAITING:
|
|
|
|
case THREAD_SUSPENDED_WAITING:
|
|
|
|
waitType = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
waitType = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
if(statusPtr != 0)
|
|
|
|
{
|
2016-01-10 22:00:18 -05:00
|
|
|
auto status = reinterpret_cast<THREADSTATUS*>(GetStructPtr(statusPtr));
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
status->status = ret;
|
|
|
|
status->initPriority = thread->initPriority;
|
|
|
|
status->currPriority = thread->currPriority;
|
|
|
|
status->stackBase = thread->stackBase;
|
|
|
|
status->stackSize = thread->stackSize;
|
|
|
|
status->waitType = waitType;
|
|
|
|
status->wakeupCount = thread->wakeUpCount;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2015-05-19 03:17:31 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = ret;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//32
|
|
|
|
void CPS2OS::sc_SleepThread()
|
|
|
|
{
|
2016-01-21 22:53:08 -05:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(m_currentThreadId);
|
|
|
|
|
2015-08-08 23:36:00 -04:00
|
|
|
auto thread = m_threads[m_currentThreadId];
|
2015-05-06 00:54:15 -04:00
|
|
|
if(thread->wakeUpCount == 0)
|
|
|
|
{
|
|
|
|
assert(thread->status == THREAD_RUNNING);
|
|
|
|
thread->status = THREAD_SLEEPING;
|
2015-08-09 00:03:42 -04:00
|
|
|
UnlinkThread(m_currentThreadId);
|
2015-05-06 00:54:15 -04:00
|
|
|
ThreadShakeAndBake();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
thread->wakeUpCount--;
|
|
|
|
}
|
|
|
|
|
|
|
|
//33
|
|
|
|
//34
|
|
|
|
void CPS2OS::sc_WakeupThread()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
bool isInt = m_ee.m_State.nGPR[3].nV[0] == 0x34;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2016-01-10 22:03:16 -05:00
|
|
|
if((id == 0) || (id == m_currentThreadId))
|
|
|
|
{
|
|
|
|
//Can't wakeup a thread that's already running
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
2015-08-09 00:03:42 -04:00
|
|
|
if(!thread)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2016-01-10 22:03:16 -05:00
|
|
|
if(thread->status == THREAD_ZOMBIE)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
if(
|
2018-04-30 21:01:23 +01:00
|
|
|
(thread->status == THREAD_SLEEPING) ||
|
|
|
|
(thread->status == THREAD_SUSPENDED_SLEEPING))
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_SLEEPING:
|
|
|
|
thread->status = THREAD_RUNNING;
|
2015-08-09 00:03:42 -04:00
|
|
|
LinkThread(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED_SLEEPING:
|
|
|
|
thread->status = THREAD_SUSPENDED;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(!isInt)
|
|
|
|
{
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
thread->wakeUpCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//35
|
|
|
|
//36
|
|
|
|
void CPS2OS::sc_CancelWakeupThread()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2023-05-02 08:56:25 -04:00
|
|
|
FRAMEWORK_MAYBE_UNUSED bool isInt = m_ee.m_State.nGPR[3].nV[0] == 0x36;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-06 01:20:04 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 wakeUpCount = thread->wakeUpCount;
|
|
|
|
thread->wakeUpCount = 0;
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = wakeUpCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
//37
|
2016-10-02 16:32:43 -04:00
|
|
|
//38
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::sc_SuspendThread()
|
|
|
|
{
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2016-10-02 16:32:43 -04:00
|
|
|
bool isInt = m_ee.m_State.nGPR[3].nV[0] == 0x38;
|
|
|
|
|
2015-08-19 21:59:46 -04:00
|
|
|
if(id == m_currentThreadId)
|
|
|
|
{
|
|
|
|
//This actually works on a real PS2
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-16 01:06:38 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-19 21:59:46 -04:00
|
|
|
if(
|
2018-04-30 21:01:23 +01:00
|
|
|
(thread->status == THREAD_ZOMBIE) ||
|
|
|
|
(thread->status == THREAD_SUSPENDED) ||
|
|
|
|
(thread->status == THREAD_SUSPENDED_WAITING) ||
|
|
|
|
(thread->status == THREAD_SUSPENDED_SLEEPING))
|
2015-08-19 21:59:46 -04:00
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_RUNNING:
|
|
|
|
thread->status = THREAD_SUSPENDED;
|
2015-08-09 00:03:42 -04:00
|
|
|
UnlinkThread(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case THREAD_WAITING:
|
|
|
|
thread->status = THREAD_SUSPENDED_WAITING;
|
|
|
|
break;
|
|
|
|
case THREAD_SLEEPING:
|
|
|
|
thread->status = THREAD_SUSPENDED_SLEEPING;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-16 01:06:38 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
|
|
|
|
2016-10-02 16:32:43 -04:00
|
|
|
if(!isInt)
|
|
|
|
{
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//39
|
2023-08-18 12:40:31 -04:00
|
|
|
//3A
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::sc_ResumeThread()
|
|
|
|
{
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2023-08-18 12:40:31 -04:00
|
|
|
bool isInt = m_ee.m_State.nGPR[3].nV[0] == 0x3A;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-19 21:59:46 -04:00
|
|
|
if(id == m_currentThreadId)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[id];
|
|
|
|
if(!thread)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2015-08-16 01:06:38 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-19 21:59:46 -04:00
|
|
|
if(
|
2018-04-30 21:01:23 +01:00
|
|
|
(thread->status == THREAD_ZOMBIE) ||
|
|
|
|
(thread->status == THREAD_RUNNING) ||
|
|
|
|
(thread->status == THREAD_WAITING) ||
|
|
|
|
(thread->status == THREAD_SLEEPING))
|
2015-08-19 21:59:46 -04:00
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_SUSPENDED:
|
|
|
|
thread->status = THREAD_RUNNING;
|
2015-08-09 00:03:42 -04:00
|
|
|
LinkThread(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED_WAITING:
|
|
|
|
thread->status = THREAD_WAITING;
|
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED_SLEEPING:
|
|
|
|
thread->status = THREAD_SLEEPING;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-16 01:06:38 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
|
|
|
|
2023-08-18 12:40:31 -04:00
|
|
|
if(!isInt)
|
|
|
|
{
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//3C
|
|
|
|
void CPS2OS::sc_SetupThread()
|
|
|
|
{
|
|
|
|
uint32 stackBase = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
uint32 stackSize = m_ee.m_State.nGPR[SC_PARAM2].nV[0];
|
|
|
|
|
|
|
|
uint32 stackAddr = 0;
|
|
|
|
if(stackBase == 0xFFFFFFFF)
|
|
|
|
{
|
2018-08-06 21:33:27 -04:00
|
|
|
//We need to substract 4k from RAM size because some games (Espgaluda) rely on the
|
|
|
|
//stack and heap being a very precise size. EE kernel seems to substract that amount too.
|
2023-06-08 17:12:43 -04:00
|
|
|
stackAddr = m_ramSize - (4 * 1024);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stackAddr = stackBase + stackSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 argsBase = m_ee.m_State.nGPR[SC_PARAM3].nV[0];
|
|
|
|
//Copy arguments
|
|
|
|
{
|
2018-10-11 12:41:37 -04:00
|
|
|
uint32 argsCount = static_cast<uint32>(m_currentArguments.size());
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
*reinterpret_cast<uint32*>(m_ram + argsBase) = argsCount;
|
|
|
|
uint32 argsPtrs = argsBase + 4;
|
|
|
|
//We add 1 to argsCount because argv[argc] must be equal to 0
|
|
|
|
uint32 argsPayload = argsPtrs + ((argsCount + 1) * 4);
|
|
|
|
for(uint32 i = 0; i < argsCount; i++)
|
|
|
|
{
|
2018-10-11 12:41:37 -04:00
|
|
|
const auto& currentArg = m_currentArguments[i];
|
2015-05-06 00:54:15 -04:00
|
|
|
*reinterpret_cast<uint32*>(m_ram + argsPtrs + (i * 4)) = argsPayload;
|
|
|
|
uint32 argSize = static_cast<uint32>(currentArg.size()) + 1;
|
|
|
|
memcpy(m_ram + argsPayload, currentArg.c_str(), argSize);
|
|
|
|
argsPayload += argSize;
|
|
|
|
}
|
|
|
|
//Set argv[argc] to 0
|
|
|
|
*reinterpret_cast<uint32*>(m_ram + argsPtrs + (argsCount * 4)) = 0;
|
|
|
|
}
|
|
|
|
|
2018-10-01 13:05:56 -04:00
|
|
|
uint32 threadId = -1;
|
|
|
|
if(
|
2018-10-04 17:07:48 -04:00
|
|
|
(m_currentThreadId == 0) ||
|
|
|
|
(m_currentThreadId == m_idleThreadId))
|
2018-10-01 13:05:56 -04:00
|
|
|
{
|
|
|
|
//No thread has been started, spawn a new thread
|
2023-11-16 17:58:37 -05:00
|
|
|
threadId = m_threads.AllocateAt(m_state->allocateThreadNextId);
|
2024-01-04 18:06:02 -05:00
|
|
|
|
|
|
|
//Some games (Metropolismania 2) expect main thread's id to be 1
|
|
|
|
assert(threadId == 1);
|
2018-10-01 13:05:56 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Re-use the calling thread (needed by 007: Nightfire)
|
|
|
|
threadId = m_currentThreadId;
|
|
|
|
UnlinkThread(threadId);
|
|
|
|
}
|
2015-08-06 01:20:04 -04:00
|
|
|
assert(static_cast<int32>(threadId) != -1);
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//Set up the main thread
|
2015-08-16 02:42:47 -04:00
|
|
|
//Priority needs to be 0 because some games rely on this
|
|
|
|
//by calling RotateThreadReadyQueue(0) (Dynasty Warriors 2)
|
2015-08-06 01:20:04 -04:00
|
|
|
auto thread = m_threads[threadId];
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->status = THREAD_RUNNING;
|
|
|
|
thread->stackBase = stackAddr - stackSize;
|
2019-01-15 17:24:06 +08:00
|
|
|
thread->stackSize = stackSize;
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->initPriority = 0;
|
|
|
|
thread->currPriority = 0;
|
|
|
|
thread->contextPtr = 0;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-09 00:03:42 -04:00
|
|
|
LinkThread(threadId);
|
2015-08-08 23:36:00 -04:00
|
|
|
m_currentThreadId = threadId;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2021-07-28 11:20:25 -04:00
|
|
|
uint32 stackPtr = stackAddr - STACKRES;
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[0] = stackPtr;
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//3D
|
|
|
|
void CPS2OS::sc_SetupHeap()
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
auto thread = m_threads[m_currentThreadId];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
uint32 heapBase = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 heapSize = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
|
|
|
|
if(heapSize == 0xFFFFFFFF)
|
|
|
|
{
|
|
|
|
thread->heapBase = thread->stackBase;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
thread->heapBase = heapBase + heapSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[0] = thread->heapBase;
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//3E
|
|
|
|
void CPS2OS::sc_EndOfHeap()
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
auto thread = m_threads[m_currentThreadId];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[0] = thread->heapBase;
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//40
|
|
|
|
void CPS2OS::sc_CreateSema()
|
|
|
|
{
|
|
|
|
auto semaParam = reinterpret_cast<SEMAPHOREPARAM*>(GetStructPtr(m_ee.m_State.nGPR[SC_PARAM0].nV0));
|
|
|
|
|
|
|
|
uint32 id = m_semaphores.Allocate();
|
2024-06-03 16:57:20 -04:00
|
|
|
if(id == SemaphoreList::INVALID_ID)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2024-06-03 16:57:20 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Failed to allocate semaphore.\r\n");
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto sema = m_semaphores[id];
|
2018-04-30 21:01:23 +01:00
|
|
|
sema->count = semaParam->initCount;
|
|
|
|
sema->maxCount = semaParam->maxCount;
|
2019-05-23 21:14:35 -04:00
|
|
|
sema->option = semaParam->option;
|
2018-04-30 21:01:23 +01:00
|
|
|
sema->waitCount = 0;
|
2021-07-22 19:11:29 -04:00
|
|
|
sema->waitNextId = 0;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
assert(sema->count <= sema->maxCount);
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
//41
|
|
|
|
void CPS2OS::sc_DeleteSema()
|
|
|
|
{
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
|
|
|
|
auto sema = m_semaphores[id];
|
|
|
|
if(sema == nullptr)
|
|
|
|
{
|
2017-01-01 18:27:51 -05:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-01 18:30:19 -05:00
|
|
|
//Set return value now because we might reschedule
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
|
|
|
|
|
|
|
//Release all threads waiting for this semaphore
|
2015-05-06 00:54:15 -04:00
|
|
|
if(sema->waitCount != 0)
|
|
|
|
{
|
2017-01-01 18:30:19 -05:00
|
|
|
while(sema->waitCount != 0)
|
|
|
|
{
|
2021-07-22 19:11:29 -04:00
|
|
|
SemaReleaseSingleThread(id, true);
|
2017-01-01 18:30:19 -05:00
|
|
|
}
|
|
|
|
ThreadShakeAndBake();
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
m_semaphores.Free(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
//42
|
|
|
|
//43
|
|
|
|
void CPS2OS::sc_SignalSema()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
bool isInt = m_ee.m_State.nGPR[3].nV[0] == 0x43;
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
auto sema = m_semaphores[id];
|
|
|
|
if(sema == nullptr)
|
|
|
|
{
|
2021-04-22 15:27:33 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Trying to signal an invalid semaphore (%d).\r\n", id);
|
2017-01-01 18:27:51 -05:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2021-12-09 15:22:19 -05:00
|
|
|
m_idleEvaluator.NotifyEvent(Ee::CIdleEvaluator::EVENT_SIGNALSEMA, id);
|
|
|
|
|
2017-01-01 18:29:32 -05:00
|
|
|
//TODO: Check maximum value
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2017-01-01 18:29:32 -05:00
|
|
|
//Set return value here because we might reschedule
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2017-01-01 18:29:32 -05:00
|
|
|
if(sema->waitCount != 0)
|
|
|
|
{
|
|
|
|
SemaReleaseSingleThread(id, false);
|
2015-05-06 00:54:15 -04:00
|
|
|
if(!isInt)
|
|
|
|
{
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sema->count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//44
|
|
|
|
void CPS2OS::sc_WaitSema()
|
|
|
|
{
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
|
|
|
|
auto sema = m_semaphores[id];
|
|
|
|
if(sema == nullptr)
|
|
|
|
{
|
2021-04-22 15:27:33 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Trying to wait an invalid semaphore (%d).\r\n", id);
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-09 15:22:19 -05:00
|
|
|
m_idleEvaluator.NotifyEvent(Ee::CIdleEvaluator::EVENT_WAITSEMA, id);
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
if(sema->count == 0)
|
|
|
|
{
|
|
|
|
//Put this thread in sleep mode and reschedule...
|
2015-08-08 23:36:00 -04:00
|
|
|
auto thread = m_threads[m_currentThreadId];
|
2015-05-06 00:54:15 -04:00
|
|
|
assert(thread->status == THREAD_RUNNING);
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->status = THREAD_WAITING;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-08-09 00:03:42 -04:00
|
|
|
UnlinkThread(m_currentThreadId);
|
2021-07-22 19:11:29 -04:00
|
|
|
SemaLinkThread(id, m_currentThreadId);
|
2015-05-06 00:54:15 -04:00
|
|
|
ThreadShakeAndBake();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sema->count != 0)
|
|
|
|
{
|
|
|
|
sema->count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
//45
|
2015-10-31 23:58:56 -04:00
|
|
|
//46
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::sc_PollSema()
|
|
|
|
{
|
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
|
|
|
|
auto sema = m_semaphores[id];
|
|
|
|
if(sema == nullptr)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sema->count == 0)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sema->count--;
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
//47
|
|
|
|
//48
|
|
|
|
void CPS2OS::sc_ReferSemaStatus()
|
|
|
|
{
|
2023-05-02 08:56:25 -04:00
|
|
|
FRAMEWORK_MAYBE_UNUSED bool isInt = m_ee.m_State.nGPR[3].nV[0] != 0x47;
|
2015-05-06 00:54:15 -04:00
|
|
|
uint32 id = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2016-06-02 22:00:41 -04:00
|
|
|
auto semaParam = reinterpret_cast<SEMAPHOREPARAM*>(GetStructPtr(m_ee.m_State.nGPR[SC_PARAM1].nV0));
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
auto sema = m_semaphores[id];
|
|
|
|
if(sema == nullptr)
|
|
|
|
{
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
semaParam->count = sema->count;
|
|
|
|
semaParam->maxCount = sema->maxCount;
|
|
|
|
semaParam->waitThreads = sema->waitCount;
|
2019-05-23 21:14:35 -04:00
|
|
|
semaParam->option = sema->option;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = id;
|
|
|
|
}
|
|
|
|
|
2018-05-15 12:44:16 -04:00
|
|
|
//4B
|
|
|
|
void CPS2OS::sc_GetOsdConfigParam()
|
|
|
|
{
|
2020-09-09 17:13:17 -04:00
|
|
|
auto language = static_cast<OSD_LANGUAGE>(CAppConfig::GetInstance().GetPreferenceInteger(PREF_SYSTEM_LANGUAGE));
|
2020-12-17 17:46:02 -05:00
|
|
|
auto widescreenOutput = CAppConfig::GetInstance().GetPreferenceBoolean(PREF_CGSHANDLER_WIDESCREEN);
|
2020-09-09 17:13:17 -04:00
|
|
|
|
|
|
|
auto configParam = make_convertible<OSDCONFIGPARAM>(0);
|
|
|
|
configParam.version = static_cast<uint32>(OSD_VERSION::V2_EXT);
|
|
|
|
configParam.jpLanguage = (language == OSD_LANGUAGE::JAPANESE) ? 0 : 1;
|
|
|
|
configParam.language = static_cast<uint32>(language);
|
2020-12-17 17:46:02 -05:00
|
|
|
configParam.screenType = static_cast<uint32>(widescreenOutput ? OSD_SCREENTYPE::RATIO_16_9 : OSD_SCREENTYPE::RATIO_4_3);
|
2020-09-09 17:13:17 -04:00
|
|
|
|
|
|
|
auto configParamPtr = reinterpret_cast<uint32*>(GetStructPtr(m_ee.m_State.nGPR[SC_PARAM0].nV0));
|
|
|
|
(*configParamPtr) = configParam;
|
2018-05-15 12:44:16 -04:00
|
|
|
}
|
|
|
|
|
2023-09-21 09:05:03 -04:00
|
|
|
//63
|
|
|
|
void CPS2OS::sc_GetCop0()
|
|
|
|
{
|
|
|
|
uint32 regId = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
assert(regId < 0x20);
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(m_ee.m_State.nCOP0[regId & 0x1F]);
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
//64
|
|
|
|
void CPS2OS::sc_FlushCache()
|
|
|
|
{
|
2023-05-02 08:56:25 -04:00
|
|
|
FRAMEWORK_MAYBE_UNUSED uint32 operationType = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//70
|
|
|
|
void CPS2OS::sc_GsGetIMR()
|
|
|
|
{
|
|
|
|
uint32 result = 0;
|
|
|
|
|
|
|
|
if(m_gs != NULL)
|
|
|
|
{
|
|
|
|
result = m_gs->ReadPrivRegister(CGSHandler::GS_IMR);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
//71
|
|
|
|
void CPS2OS::sc_GsPutIMR()
|
|
|
|
{
|
|
|
|
uint32 imr = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
|
|
|
|
if(m_gs != NULL)
|
|
|
|
{
|
2018-10-24 20:18:13 -04:00
|
|
|
m_gs->WritePrivRegister(CGSHandler::GS_IMR + 0, imr);
|
|
|
|
m_gs->WritePrivRegister(CGSHandler::GS_IMR + 4, 0);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//73
|
|
|
|
void CPS2OS::sc_SetVSyncFlag()
|
|
|
|
{
|
2015-05-15 19:14:03 -04:00
|
|
|
uint32 ptr1 = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 ptr2 = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2015-05-15 19:14:03 -04:00
|
|
|
SetVsyncFlagPtrs(ptr1, ptr2);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[0] = 0;
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//74
|
|
|
|
void CPS2OS::sc_SetSyscall()
|
|
|
|
{
|
|
|
|
uint32 number = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 address = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
|
|
|
|
if(number < 0x100)
|
|
|
|
{
|
|
|
|
GetCustomSyscallTable()[number] = address;
|
|
|
|
}
|
|
|
|
else if(number == 0x12C)
|
|
|
|
{
|
|
|
|
//This syscall doesn't do any bounds checking and it's possible to
|
|
|
|
//set INTC handler for timer 3 through this system call (a particular version of libcdvd does this)
|
|
|
|
//My guess is that the BIOS doesn't process custom INTC handlers for INT12 (timer 3) and the
|
|
|
|
//only way to have an handler placed on that interrupt line is to do that trick.
|
|
|
|
|
|
|
|
unsigned int line = 12;
|
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
uint32 handlerId = m_intcHandlers.Allocate();
|
|
|
|
if(static_cast<int32>(handlerId) == -1)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2018-05-31 21:45:42 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Couldn't set INTC handler through SetSyscall");
|
2015-05-06 00:54:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-25 20:13:59 -04:00
|
|
|
auto handler = m_intcHandlers[handlerId];
|
2018-04-30 21:01:23 +01:00
|
|
|
handler->address = address & 0x1FFFFFFF;
|
|
|
|
handler->cause = line;
|
|
|
|
handler->arg = 0;
|
|
|
|
handler->gp = 0;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
if(!(m_ee.m_pMemoryMap->GetWord(CINTC::INTC_MASK) & (1 << line)))
|
|
|
|
{
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CINTC::INTC_MASK, (1 << line));
|
|
|
|
}
|
2015-07-25 20:13:59 -04:00
|
|
|
|
|
|
|
m_intcHandlerQueue.PushFront(handlerId);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-05-31 21:45:42 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Unknown syscall set (%d).\r\n", number);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[0] = 0;
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//76
|
|
|
|
void CPS2OS::sc_SifDmaStat()
|
|
|
|
{
|
2019-01-15 10:11:36 +08:00
|
|
|
uint32 queueId = m_ee.m_State.nGPR[SC_PARAM0].nV0;
|
|
|
|
uint32 queueIdx = queueId - BIOS_ID_BASE;
|
|
|
|
if(queueIdx >= BIOS_SIFDMA_COUNT)
|
|
|
|
{
|
|
|
|
//Act as if it was completed
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-10-30 15:29:44 -04:00
|
|
|
//If SIF dma has just been set (1000 cycle delay), return 'queued' status.
|
|
|
|
//This is required for Okami & God Hand (Clover Studio games) which expect to see the queued status.
|
2019-01-15 10:11:36 +08:00
|
|
|
int64 timerDiff = static_cast<uint64>(m_ee.m_State.nCOP0[CCOP_SCU::COUNT]) - static_cast<uint64>(m_sifDmaTimes[queueIdx]);
|
2023-10-30 15:29:44 -04:00
|
|
|
if((timerDiff < 0) || (timerDiff > 1000))
|
2016-05-28 21:07:00 -04:00
|
|
|
{
|
|
|
|
//Completed
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(-1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Queued
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(1);
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//77
|
|
|
|
void CPS2OS::sc_SifSetDma()
|
|
|
|
{
|
2019-01-15 10:11:36 +08:00
|
|
|
uint32 queueIdx = m_sifDmaNextIdx;
|
|
|
|
m_sifDmaTimes[queueIdx] = m_ee.m_State.nCOP0[CCOP_SCU::COUNT];
|
|
|
|
m_sifDmaNextIdx = (m_sifDmaNextIdx + 1) % BIOS_SIFDMA_COUNT;
|
2016-05-28 21:07:00 -04:00
|
|
|
|
2017-01-26 23:31:31 -05:00
|
|
|
auto xfers = reinterpret_cast<const SIFDMAREG*>(GetStructPtr(m_ee.m_State.nGPR[SC_PARAM0].nV0));
|
2015-05-06 00:54:15 -04:00
|
|
|
uint32 count = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
|
2022-02-14 10:30:34 -05:00
|
|
|
//SifSetDma seems to always send something
|
|
|
|
if(count == 0) count = 1;
|
|
|
|
|
2019-01-15 10:11:36 +08:00
|
|
|
//DMA might call an interrupt handler, set return value now
|
|
|
|
uint32 queueId = queueIdx + BIOS_ID_BASE;
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(queueId);
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
for(unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
2017-01-26 23:31:31 -05:00
|
|
|
const auto& xfer = xfers[i];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2017-01-26 23:31:31 -05:00
|
|
|
uint32 size = (xfer.size + 0x0F) / 0x10;
|
|
|
|
assert((xfer.srcAddr & 0x0F) == 0);
|
|
|
|
assert((xfer.dstAddr & 0x03) == 0);
|
|
|
|
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CDMAC::D6_MADR, xfer.srcAddr);
|
|
|
|
m_ee.m_pMemoryMap->SetWord(CDMAC::D6_TADR, xfer.dstAddr);
|
2018-04-30 21:01:23 +01:00
|
|
|
m_ee.m_pMemoryMap->SetWord(CDMAC::D6_QWC, size);
|
2017-01-26 23:31:31 -05:00
|
|
|
m_ee.m_pMemoryMap->SetWord(CDMAC::D6_CHCR, CDMAC::CHCR_BIT::CHCR_STR);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//78
|
|
|
|
void CPS2OS::sc_SifSetDChain()
|
|
|
|
{
|
|
|
|
//Humm, set the SIF0 DMA channel in destination chain mode?
|
|
|
|
|
|
|
|
//This syscall is invoked by the SIF dma interrupt handler (when a packet has been received)
|
2018-04-30 21:01:23 +01:00
|
|
|
//To make sure every packet generates an interrupt, the SIF will hold into any packet
|
2015-05-06 00:54:15 -04:00
|
|
|
//that it might have in its queue while a packet is being processed.
|
|
|
|
//The MarkPacketProcess function will tell the SIF that the packet has been processed
|
|
|
|
//and that it can continue sending packets over. (Needed for Guilty Gear XX Accent Core Plus)
|
|
|
|
bool isInt = m_ee.m_State.nGPR[3].nV[1] == ~0;
|
|
|
|
if(isInt)
|
|
|
|
{
|
|
|
|
m_sif.MarkPacketProcessed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//79
|
|
|
|
void CPS2OS::sc_SifSetReg()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 registerId = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 value = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
m_sif.SetRegister(registerId, value);
|
|
|
|
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//7A
|
|
|
|
void CPS2OS::sc_SifGetReg()
|
|
|
|
{
|
|
|
|
uint32 registerId = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(m_sif.GetRegister(registerId));
|
|
|
|
}
|
|
|
|
|
|
|
|
//7C
|
|
|
|
void CPS2OS::sc_Deci2Call()
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
uint32 function = m_ee.m_State.nGPR[SC_PARAM0].nV[0];
|
|
|
|
uint32 param = m_ee.m_State.nGPR[SC_PARAM1].nV[0];
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
switch(function)
|
|
|
|
{
|
|
|
|
case 0x01:
|
|
|
|
//Deci2Open
|
|
|
|
{
|
2024-06-04 17:20:45 -04:00
|
|
|
uint32 id = m_deci2Handlers.Allocate();
|
|
|
|
if(id == Deci2HandlerList::INVALID_ID)
|
|
|
|
{
|
|
|
|
//This happens in 007: Nightfire. The game has a custom ELF loader and newly loaded
|
|
|
|
//ELF attempts to clean everything, but they forgot about DECI2 handlers.
|
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Failed to allocate DECI2 handler.\r\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto handler = m_deci2Handlers[id];
|
|
|
|
handler->isValid = 1;
|
|
|
|
handler->device = *reinterpret_cast<uint32*>(GetStructPtr(param + 0x00));
|
|
|
|
handler->bufferAddr = *reinterpret_cast<uint32*>(GetStructPtr(param + 0x04));
|
|
|
|
}
|
2023-09-01 16:53:18 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(id);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x03:
|
|
|
|
//Deci2Send
|
|
|
|
{
|
2016-06-27 20:16:32 -04:00
|
|
|
uint32 id = *reinterpret_cast<uint32*>(GetStructPtr(param + 0x00));
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2024-06-04 17:20:45 -04:00
|
|
|
auto handler = m_deci2Handlers[id];
|
|
|
|
assert(handler);
|
|
|
|
if(handler)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2023-09-01 16:53:18 -04:00
|
|
|
auto buffer = reinterpret_cast<DECI2BUFFER*>(GetStructPtr(handler->bufferAddr));
|
|
|
|
if(buffer->dataAddr != 0)
|
|
|
|
{
|
|
|
|
auto sendInfo = reinterpret_cast<DECI2SEND*>(GetStructPtr(buffer->dataAddr));
|
|
|
|
assert(sendInfo->size >= 0x0C);
|
|
|
|
if(sendInfo->size >= 0x0C)
|
|
|
|
{
|
|
|
|
m_iopBios.GetIoman()->Write(Iop::CIoman::FID_STDOUT, sendInfo->size - 0xC, sendInfo->data);
|
|
|
|
}
|
|
|
|
buffer->status0 = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Report an error. F1 2002 seems to send something wrong and expects some error.
|
|
|
|
buffer->status0 = -1;
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2023-09-01 16:53:18 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = 1;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
//Deci2Poll
|
|
|
|
{
|
2016-06-27 20:16:32 -04:00
|
|
|
uint32 id = *reinterpret_cast<uint32*>(GetStructPtr(param + 0x00));
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2024-06-04 17:20:45 -04:00
|
|
|
auto handler = m_deci2Handlers[id];
|
|
|
|
assert(handler);
|
|
|
|
if(handler)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2023-09-01 16:53:18 -04:00
|
|
|
auto buffer = reinterpret_cast<DECI2BUFFER*>(GetStructPtr(handler->bufferAddr));
|
|
|
|
buffer->status1 = 0;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2023-09-01 16:53:18 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nD0 = 1;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x10:
|
|
|
|
//kPuts
|
|
|
|
{
|
2017-01-28 19:49:50 -05:00
|
|
|
uint32 stringAddr = *reinterpret_cast<uint32*>(GetStructPtr(param));
|
2015-05-06 00:54:15 -04:00
|
|
|
uint8* string = &m_ram[stringAddr];
|
|
|
|
m_iopBios.GetIoman()->Write(1, static_cast<uint32>(strlen(reinterpret_cast<char*>(string))), string);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2018-05-31 21:45:42 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "Unknown Deci2Call function (0x%08X) called. PC: 0x%08X.\r\n", function, m_ee.m_State.nPC);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//7E
|
|
|
|
void CPS2OS::sc_MachineType()
|
|
|
|
{
|
|
|
|
//Return 0x100 for liberx (is this ok?)
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[0] = 0x100;
|
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//7F
|
|
|
|
void CPS2OS::sc_GetMemorySize()
|
|
|
|
{
|
2023-06-08 17:12:43 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[0] = m_ramSize;
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nGPR[SC_RETURN].nV[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
//System Call Handler
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void CPS2OS::HandleSyscall()
|
|
|
|
{
|
|
|
|
uint32 searchAddress = m_ee.m_State.nCOP0[CCOP_SCU::EPC];
|
|
|
|
uint32 callInstruction = m_ee.m_pMemoryMap->GetInstruction(searchAddress);
|
|
|
|
if(callInstruction != 0x0000000C)
|
|
|
|
{
|
|
|
|
//This will happen if an ADDIU R0, R0, $x instruction is encountered. Not sure if there's a use for that on the EE
|
2018-05-31 21:45:42 -04:00
|
|
|
CLog::GetInstance().Warn(LOG_NAME, "System call exception occured but no SYSCALL instruction found (addr = 0x%08X, opcode = 0x%08X).\r\n",
|
|
|
|
searchAddress, callInstruction);
|
2015-05-06 00:54:15 -04:00
|
|
|
m_ee.m_State.nHasException = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-21 08:52:48 -04:00
|
|
|
uint32 func = m_ee.m_State.nGPR[CMIPS::V1].nV[0];
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2019-02-09 11:16:59 -05:00
|
|
|
if(func == SYSCALL_CUSTOM_RESCHEDULE)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
|
|
|
//Reschedule
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
2019-02-09 11:26:48 -05:00
|
|
|
else if(func == SYSCALL_CUSTOM_EXITINTERRUPT)
|
|
|
|
{
|
|
|
|
//Execute ERET
|
|
|
|
m_ee.m_State.nCOP0[CCOP_SCU::STATUS] &= ~(CMIPS::STATUS_EXL);
|
|
|
|
m_ee.m_State.nPC = m_ee.m_State.nGPR[CMIPS::A0].nV0;
|
|
|
|
|
|
|
|
if(m_currentThreadId != m_idleThreadId)
|
|
|
|
{
|
|
|
|
auto thread = m_threads[m_currentThreadId];
|
|
|
|
ThreadLoadContext(thread, true);
|
|
|
|
}
|
|
|
|
ThreadShakeAndBake();
|
|
|
|
}
|
2020-03-25 08:02:59 -04:00
|
|
|
else if((func >= Ee::CLibMc2::SYSCALL_RANGE_START) && (func < Ee::CLibMc2::SYSCALL_RANGE_END))
|
|
|
|
{
|
|
|
|
m_libMc2.HandleSyscall(m_ee);
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if(func & 0x80000000)
|
|
|
|
{
|
|
|
|
func = 0 - func;
|
|
|
|
}
|
|
|
|
//Save for custom handler
|
|
|
|
m_ee.m_State.nGPR[3].nV[0] = func;
|
|
|
|
|
2015-10-23 23:18:32 -04:00
|
|
|
if(GetCustomSyscallTable()[func] == 0)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
#ifdef _DEBUG
|
2015-05-06 00:54:15 -04:00
|
|
|
DisassembleSysCall(static_cast<uint8>(func & 0xFF));
|
2018-04-30 21:01:23 +01:00
|
|
|
#endif
|
2015-05-06 00:54:15 -04:00
|
|
|
if(func < 0x80)
|
|
|
|
{
|
|
|
|
((this)->*(m_sysCall[func & 0xFF]))();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_ee.GenerateException(0x1FC00100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-17 17:39:49 -04:00
|
|
|
m_ee.m_State.nHasException = MIPS_EXCEPTION_NONE;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
2020-08-04 13:29:59 -04:00
|
|
|
void CPS2OS::HandleTLBException()
|
|
|
|
{
|
|
|
|
assert(m_ee.CanGenerateInterrupt());
|
|
|
|
m_ee.m_State.nCOP0[CCOP_SCU::STATUS] |= CMIPS::STATUS_EXL;
|
|
|
|
assert(m_ee.m_State.nDelayedJumpAddr == MIPS_INVALID_PC);
|
|
|
|
|
|
|
|
uint32 excCode = m_ee.m_State.nCOP0[CCOP_SCU::CAUSE] & CCOP_SCU::CAUSE_EXCCODE_MASK;
|
|
|
|
switch(excCode)
|
|
|
|
{
|
|
|
|
case CCOP_SCU::CAUSE_EXCCODE_TLBL:
|
|
|
|
m_ee.m_State.nPC = m_tlblExceptionHandler;
|
|
|
|
break;
|
|
|
|
case CCOP_SCU::CAUSE_EXCCODE_TLBS:
|
|
|
|
m_ee.m_State.nPC = m_tlbsExceptionHandler;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//Can't handle other types of exceptions here
|
|
|
|
assert(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m_ee.m_State.nHasException = MIPS_EXCEPTION_NONE;
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:54:15 -04:00
|
|
|
void CPS2OS::DisassembleSysCall(uint8 func)
|
|
|
|
{
|
2025-02-12 14:12:37 -05:00
|
|
|
#if LOGGING_ENABLED
|
2015-05-06 00:54:15 -04:00
|
|
|
std::string description(GetSysCallDescription(func));
|
|
|
|
if(description.length() != 0)
|
|
|
|
{
|
2015-08-08 23:36:00 -04:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "%d: %s\r\n", m_currentThreadId.Get(), description.c_str());
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CPS2OS::GetSysCallDescription(uint8 function)
|
|
|
|
{
|
2023-09-21 08:54:55 -04:00
|
|
|
std::string description;
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
switch(function)
|
|
|
|
{
|
|
|
|
case 0x02:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format("GsSetCrt(interlace = %i, mode = %i, field = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM2].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x04:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_EXIT "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x06:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_LOADEXECPS2 "(exec = 0x%08X, argc = %d, argv = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM2].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
2015-06-20 02:15:55 -04:00
|
|
|
case 0x07:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_EXECPS2 "(pc = 0x%08X, gp = 0x%08X, argc = %d, argv = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM2].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM3].nV[0]);
|
2015-06-20 02:15:55 -04:00
|
|
|
break;
|
2018-08-27 07:31:18 -04:00
|
|
|
case 0x0D:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SETVTLBREFILLHANDLER "(cause = %d, handler = 0x%08x);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2018-08-27 07:31:18 -04:00
|
|
|
break;
|
|
|
|
case 0x0E:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SETVCOMMONHANDLER "(cause = %d, handler = 0x%08x);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2018-08-27 07:31:18 -04:00
|
|
|
break;
|
2015-05-06 00:54:15 -04:00
|
|
|
case 0x10:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ADDINTCHANDLER "(cause = %i, address = 0x%08X, next = 0x%08X, arg = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM2].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM3].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x11:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_REMOVEINTCHANDLER "(cause = %i, id = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x12:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ADDDMACHANDLER "(channel = %i, address = 0x%08X, next = %i, arg = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM2].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM3].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x13:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_REMOVEDMACHANDLER "(channel = %i, handler = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x14:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ENABLEINTC "(cause = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x15:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_DISABLEINTC "(cause = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x16:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ENABLEDMAC "(channel = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x17:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_DISABLEDMAC "(channel = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x18:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SETALARM "(time = %d, proc = 0x%08X, arg = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM2].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
2016-09-25 14:53:30 -04:00
|
|
|
case 0x1A:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IENABLEINTC "(cause = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2016-09-25 14:53:30 -04:00
|
|
|
break;
|
|
|
|
case 0x1B:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IDISABLEINTC "(cause = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2016-09-25 14:53:30 -04:00
|
|
|
break;
|
2016-08-29 22:15:29 +09:00
|
|
|
case 0x1C:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IENABLEDMAC "(channel = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2016-08-29 22:15:29 +09:00
|
|
|
break;
|
|
|
|
case 0x1D:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IDISABLEDMAC "(channel = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2016-08-29 22:15:29 +09:00
|
|
|
break;
|
2019-08-16 07:49:13 -04:00
|
|
|
case 0x1E:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ISETALARM "(time = %d, proc = 0x%08X, arg = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM2].nV[0]);
|
2019-08-16 07:49:13 -04:00
|
|
|
break;
|
2015-05-06 00:54:15 -04:00
|
|
|
case 0x1F:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IRELEASEALARM "(id = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x20:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_CREATETHREAD "(thread = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x21:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_DELETETHREAD "(id = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x22:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_STARTTHREAD "(id = 0x%08X, a0 = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x23:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_EXITTHREAD "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
2015-06-03 04:05:59 -04:00
|
|
|
case 0x24:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_EXITDELETETHREAD "();");
|
2015-06-03 04:05:59 -04:00
|
|
|
break;
|
2015-05-06 00:54:15 -04:00
|
|
|
case 0x25:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_TERMINATETHREAD "(id = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x29:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_CHANGETHREADPRIORITY "(id = 0x%08X, priority = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x2A:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ICHANGETHREADPRIORITY "(id = 0x%08X, priority = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x2B:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ROTATETHREADREADYQUEUE "(prio = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
2018-06-19 19:12:34 -04:00
|
|
|
case 0x2D:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_RELEASEWAITTHREAD "(id = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2018-06-19 19:12:34 -04:00
|
|
|
break;
|
|
|
|
case 0x2E:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IRELEASEWAITTHREAD "(id = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2018-06-19 19:12:34 -04:00
|
|
|
break;
|
2015-05-06 00:54:15 -04:00
|
|
|
case 0x2F:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_GETTHREADID "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x30:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_REFERTHREADSTATUS "(threadId = %d, infoPtr = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x31:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IREFERTHREADSTATUS "(threadId = %d, infoPtr = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x32:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SLEEPTHREAD "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x33:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_WAKEUPTHREAD "(id = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x34:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IWAKEUPTHREAD "(id = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x35:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_CANCELWAKEUPTHREAD "(id = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x36:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ICANCELWAKEUPTHREAD "(id = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x37:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SUSPENDTHREAD "(id = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
2016-10-02 16:32:43 -04:00
|
|
|
case 0x38:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ISUSPENDTHREAD "(id = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2016-10-02 16:32:43 -04:00
|
|
|
break;
|
2015-05-06 00:54:15 -04:00
|
|
|
case 0x39:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_RESUMETHREAD "(id = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x3C:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format("SetupThread(gp = 0x%08X, stack = 0x%08X, stack_size = 0x%08X, args = 0x%08X, root_func = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM2].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM3].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM4].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x3D:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format("SetupHeap(heap_start = 0x%08X, heap_size = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x3E:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ENDOFHEAP "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x40:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_CREATESEMA "(sema = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x41:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_DELETESEMA "(semaid = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x42:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SIGNALSEMA "(semaid = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x43:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_ISIGNALSEMA "(semaid = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x44:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_WAITSEMA "(semaid = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x45:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_POLLSEMA "(semaid = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x46:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IPOLLSEMA "(semaid = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x47:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_REFERSEMASTATUS "(semaid = %i, status = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x48:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_IREFERSEMASTATUS "(semaid = %i, status = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
2018-05-15 12:44:16 -04:00
|
|
|
case 0x4B:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_GETOSDCONFIGPARAM "(configPtr = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2018-05-15 12:44:16 -04:00
|
|
|
break;
|
2023-09-21 09:05:03 -04:00
|
|
|
case 0x63:
|
|
|
|
description = string_format(SYSCALL_NAME_GETCOP0 "(reg = %d);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
|
|
|
break;
|
2015-05-06 00:54:15 -04:00
|
|
|
case 0x64:
|
|
|
|
case 0x68:
|
|
|
|
#ifdef _DEBUG
|
2023-09-21 08:54:55 -04:00
|
|
|
// description = string_format(SYSCALL_NAME_FLUSHCACHE "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case 0x70:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_GSGETIMR "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x71:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_GSPUTIMR "(GS_IMR = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x73:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SETVSYNCFLAG "(ptr1 = 0x%08X, ptr2 = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x74:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SETSYSCALL "(num = 0x%02X, address = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x76:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SIFDMASTAT "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x77:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SIFSETDMA "(list = 0x%08X, count = %i);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x78:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_SIFSETDCHAIN "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x79:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format("SifSetReg(register = 0x%08X, value = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x7A:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format("SifGetReg(register = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x7C:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_DECI2CALL "(func = 0x%08X, param = 0x%08X);",
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM0].nV[0],
|
|
|
|
m_ee.m_State.nGPR[SC_PARAM1].nV[0]);
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x7E:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format(SYSCALL_NAME_MACHINETYPE "();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
case 0x7F:
|
2023-09-21 08:54:55 -04:00
|
|
|
description = string_format("GetMemorySize();");
|
2015-05-06 00:54:15 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-09-21 08:54:55 -04:00
|
|
|
return description;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
//System Call Handlers Table
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
|
2018-04-30 11:19:06 -04:00
|
|
|
// clang-format off
|
2015-05-06 00:54:15 -04:00
|
|
|
CPS2OS::SystemCallHandler CPS2OS::m_sysCall[0x80] =
|
|
|
|
{
|
|
|
|
//0x00
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_GsSetCrt, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Exit, &CPS2OS::sc_Unhandled, &CPS2OS::sc_LoadExecPS2, &CPS2OS::sc_ExecPS2,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x08
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_SetVTLBRefillHandler, &CPS2OS::sc_SetVCommonHandler, &CPS2OS::sc_Unhandled,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x10
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_AddIntcHandler, &CPS2OS::sc_RemoveIntcHandler, &CPS2OS::sc_AddDmacHandler, &CPS2OS::sc_RemoveDmacHandler, &CPS2OS::sc_EnableIntc, &CPS2OS::sc_DisableIntc, &CPS2OS::sc_EnableDmac, &CPS2OS::sc_DisableDmac,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x18
|
2019-08-16 07:49:13 -04:00
|
|
|
&CPS2OS::sc_SetAlarm, &CPS2OS::sc_Unhandled, &CPS2OS::sc_EnableIntc, &CPS2OS::sc_DisableIntc, &CPS2OS::sc_EnableDmac, &CPS2OS::sc_DisableDmac, &CPS2OS::sc_SetAlarm, &CPS2OS::sc_ReleaseAlarm,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x20
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_CreateThread, &CPS2OS::sc_DeleteThread, &CPS2OS::sc_StartThread, &CPS2OS::sc_ExitThread, &CPS2OS::sc_ExitDeleteThread, &CPS2OS::sc_TerminateThread, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x28
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_Unhandled, &CPS2OS::sc_ChangeThreadPriority, &CPS2OS::sc_ChangeThreadPriority, &CPS2OS::sc_RotateThreadReadyQueue, &CPS2OS::sc_Unhandled, &CPS2OS::sc_ReleaseWaitThread, &CPS2OS::sc_ReleaseWaitThread, &CPS2OS::sc_GetThreadId,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x30
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_ReferThreadStatus, &CPS2OS::sc_ReferThreadStatus, &CPS2OS::sc_SleepThread, &CPS2OS::sc_WakeupThread, &CPS2OS::sc_WakeupThread, &CPS2OS::sc_CancelWakeupThread, &CPS2OS::sc_CancelWakeupThread, &CPS2OS::sc_SuspendThread,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x38
|
2023-08-18 12:40:31 -04:00
|
|
|
&CPS2OS::sc_SuspendThread, &CPS2OS::sc_ResumeThread, &CPS2OS::sc_ResumeThread, &CPS2OS::sc_Unhandled, &CPS2OS::sc_SetupThread, &CPS2OS::sc_SetupHeap, &CPS2OS::sc_EndOfHeap, &CPS2OS::sc_Unhandled,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x40
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_CreateSema, &CPS2OS::sc_DeleteSema, &CPS2OS::sc_SignalSema, &CPS2OS::sc_SignalSema, &CPS2OS::sc_WaitSema, &CPS2OS::sc_PollSema, &CPS2OS::sc_PollSema, &CPS2OS::sc_ReferSemaStatus,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x48
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_ReferSemaStatus, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_GetOsdConfigParam, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x50
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x58
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x60
|
2023-09-21 09:05:03 -04:00
|
|
|
&CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_GetCop0, &CPS2OS::sc_FlushCache, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x68
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_FlushCache, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Unhandled,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x70
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_GsGetIMR, &CPS2OS::sc_GsPutIMR, &CPS2OS::sc_Unhandled, &CPS2OS::sc_SetVSyncFlag, &CPS2OS::sc_SetSyscall, &CPS2OS::sc_Unhandled, &CPS2OS::sc_SifDmaStat, &CPS2OS::sc_SifSetDma,
|
2015-05-06 00:54:15 -04:00
|
|
|
//0x78
|
2018-08-27 07:31:18 -04:00
|
|
|
&CPS2OS::sc_SifSetDChain, &CPS2OS::sc_SifSetReg, &CPS2OS::sc_SifGetReg, &CPS2OS::sc_Unhandled, &CPS2OS::sc_Deci2Call, &CPS2OS::sc_Unhandled, &CPS2OS::sc_MachineType, &CPS2OS::sc_GetMemorySize,
|
2015-05-06 00:54:15 -04:00
|
|
|
};
|
2018-04-30 11:19:06 -04:00
|
|
|
// clang-format on
|
2015-05-06 00:54:15 -04:00
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Debug Stuff
|
|
|
|
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
|
|
|
|
BiosDebugModuleInfoArray CPS2OS::GetModulesDebugInfo() const
|
|
|
|
{
|
|
|
|
BiosDebugModuleInfoArray result;
|
|
|
|
|
|
|
|
if(m_elf)
|
|
|
|
{
|
|
|
|
auto executableRange = GetExecutableRange();
|
|
|
|
|
|
|
|
BIOS_DEBUG_MODULE_INFO module;
|
2018-04-30 21:01:23 +01:00
|
|
|
module.name = m_executableName;
|
|
|
|
module.begin = executableRange.first;
|
|
|
|
module.end = executableRange.second;
|
2020-09-09 10:58:50 -04:00
|
|
|
module.param = m_elf.get();
|
2015-05-06 00:54:15 -04:00
|
|
|
result.push_back(module);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-09-08 19:17:13 -04:00
|
|
|
enum EE_BIOS_DEBUG_OBJECT_TYPE
|
|
|
|
{
|
|
|
|
EE_BIOS_DEBUG_OBJECT_TYPE_INTCHANDLER = BIOS_DEBUG_OBJECT_TYPE_CUSTOM_START,
|
|
|
|
EE_BIOS_DEBUG_OBJECT_TYPE_DMACHANDLER,
|
|
|
|
EE_BIOS_DEBUG_OBJECT_TYPE_SEMAPHORE,
|
|
|
|
};
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2022-09-07 19:58:01 -04:00
|
|
|
BiosDebugObjectInfoMap CPS2OS::GetBiosObjectsDebugInfo() const
|
2022-09-06 19:40:46 -04:00
|
|
|
{
|
2022-09-10 12:47:16 -04:00
|
|
|
static BiosDebugObjectInfoMap objectDebugInfo = [] {
|
2022-09-07 19:58:01 -04:00
|
|
|
BiosDebugObjectInfoMap result;
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2022-09-06 19:40:46 -04:00
|
|
|
BIOS_DEBUG_OBJECT_INFO info;
|
|
|
|
info.name = "Semaphores";
|
|
|
|
info.fields =
|
2022-09-10 12:47:16 -04:00
|
|
|
{
|
|
|
|
{"Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::IDENTIFIER},
|
|
|
|
{"Option", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::POSSIBLE_STR_POINTER},
|
|
|
|
{"Count", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
{"Max Count", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
{"Wait Count", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
};
|
2022-09-08 19:17:13 -04:00
|
|
|
result.emplace(std::make_pair(EE_BIOS_DEBUG_OBJECT_TYPE_SEMAPHORE, std::move(info)));
|
2022-09-06 19:40:46 -04:00
|
|
|
}
|
|
|
|
{
|
|
|
|
BIOS_DEBUG_OBJECT_INFO info;
|
|
|
|
info.name = "INTC Handlers";
|
2022-09-08 19:17:13 -04:00
|
|
|
info.selectionAction = BIOS_DEBUG_OBJECT_ACTION::SHOW_LOCATION;
|
2022-09-06 19:40:46 -04:00
|
|
|
info.fields =
|
2022-09-10 12:47:16 -04:00
|
|
|
{
|
|
|
|
{"Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::IDENTIFIER},
|
|
|
|
{"Cause", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
{"Address", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::LOCATION | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::TEXT_ADDRESS},
|
|
|
|
{"Parameter", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
{"GP", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::DATA_ADDRESS},
|
|
|
|
{"Next Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
};
|
2022-09-08 19:17:13 -04:00
|
|
|
result.emplace(std::make_pair(EE_BIOS_DEBUG_OBJECT_TYPE_INTCHANDLER, std::move(info)));
|
2022-09-06 19:40:46 -04:00
|
|
|
}
|
|
|
|
{
|
|
|
|
BIOS_DEBUG_OBJECT_INFO info;
|
|
|
|
info.name = "DMAC Handlers";
|
2022-09-08 19:17:13 -04:00
|
|
|
info.selectionAction = BIOS_DEBUG_OBJECT_ACTION::SHOW_LOCATION;
|
2022-09-06 19:40:46 -04:00
|
|
|
info.fields =
|
2022-09-10 12:47:16 -04:00
|
|
|
{
|
|
|
|
{"Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::IDENTIFIER},
|
|
|
|
{"Channel", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
{"Address", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::LOCATION | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::TEXT_ADDRESS},
|
|
|
|
{"Parameter", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
{"GP", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::DATA_ADDRESS},
|
|
|
|
{"Next Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
};
|
2022-09-08 19:17:13 -04:00
|
|
|
result.emplace(std::make_pair(EE_BIOS_DEBUG_OBJECT_TYPE_DMACHANDLER, std::move(info)));
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
{
|
2022-09-06 19:40:46 -04:00
|
|
|
BIOS_DEBUG_OBJECT_INFO info;
|
|
|
|
info.name = "Threads";
|
|
|
|
info.selectionAction = BIOS_DEBUG_OBJECT_ACTION::SHOW_STACK_OR_LOCATION;
|
|
|
|
info.fields =
|
2022-09-10 12:47:16 -04:00
|
|
|
{
|
|
|
|
{"Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::IDENTIFIER},
|
|
|
|
{"Priority", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
{"Location", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::LOCATION | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::TEXT_ADDRESS},
|
|
|
|
{"RA", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::RETURN_ADDRESS | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::HIDDEN},
|
|
|
|
{"SP", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::STACK_POINTER | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::HIDDEN},
|
2023-07-09 11:13:25 -04:00
|
|
|
{"Entry Point", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::TEXT_ADDRESS},
|
2022-09-10 12:47:16 -04:00
|
|
|
{"State", BIOS_DEBUG_OBJECT_FIELD_TYPE::STRING},
|
|
|
|
};
|
2022-09-08 19:17:13 -04:00
|
|
|
result.emplace(std::make_pair(BIOS_DEBUG_OBJECT_TYPE_THREAD, std::move(info)));
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
2022-09-06 19:40:46 -04:00
|
|
|
return result;
|
|
|
|
}();
|
|
|
|
return objectDebugInfo;
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2022-09-06 19:40:46 -04:00
|
|
|
BiosDebugObjectArray CPS2OS::GetBiosObjects(uint32 typeId) const
|
|
|
|
{
|
|
|
|
BiosDebugObjectArray result;
|
|
|
|
switch(typeId)
|
|
|
|
{
|
2022-09-08 19:17:13 -04:00
|
|
|
case EE_BIOS_DEBUG_OBJECT_TYPE_SEMAPHORE:
|
2022-09-06 19:40:46 -04:00
|
|
|
for(auto it = std::begin(m_semaphores); it != std::end(m_semaphores); it++)
|
2015-05-06 00:54:15 -04:00
|
|
|
{
|
2022-09-06 19:40:46 -04:00
|
|
|
auto semaphore = (*it);
|
|
|
|
if(!semaphore) continue;
|
|
|
|
BIOS_DEBUG_OBJECT obj;
|
|
|
|
obj.fields = {
|
2022-09-10 12:47:16 -04:00
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, it),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, semaphore->option),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, semaphore->count),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, semaphore->maxCount),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, semaphore->waitCount),
|
2022-09-06 19:40:46 -04:00
|
|
|
};
|
|
|
|
result.push_back(obj);
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
2022-09-06 19:40:46 -04:00
|
|
|
break;
|
2022-09-08 19:17:13 -04:00
|
|
|
case EE_BIOS_DEBUG_OBJECT_TYPE_INTCHANDLER:
|
2022-09-06 19:40:46 -04:00
|
|
|
for(auto it = std::begin(m_intcHandlers); it != std::end(m_intcHandlers); it++)
|
|
|
|
{
|
|
|
|
auto handler = (*it);
|
|
|
|
if(!handler) continue;
|
|
|
|
BIOS_DEBUG_OBJECT obj;
|
|
|
|
obj.fields = {
|
2022-09-10 12:47:16 -04:00
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, it),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->cause),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->address),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->arg),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->gp),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->nextId),
|
2022-09-06 19:40:46 -04:00
|
|
|
};
|
|
|
|
result.push_back(obj);
|
|
|
|
}
|
|
|
|
break;
|
2022-09-08 19:17:13 -04:00
|
|
|
case EE_BIOS_DEBUG_OBJECT_TYPE_DMACHANDLER:
|
2022-09-06 19:40:46 -04:00
|
|
|
for(auto it = std::begin(m_dmacHandlers); it != std::end(m_dmacHandlers); it++)
|
|
|
|
{
|
|
|
|
auto handler = (*it);
|
|
|
|
if(!handler) continue;
|
|
|
|
BIOS_DEBUG_OBJECT obj;
|
|
|
|
obj.fields = {
|
2022-09-10 12:47:16 -04:00
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, it),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->channel),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->address),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->arg),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->gp),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, handler->nextId),
|
2022-09-06 19:40:46 -04:00
|
|
|
};
|
|
|
|
result.push_back(obj);
|
|
|
|
}
|
|
|
|
break;
|
2022-09-08 19:17:13 -04:00
|
|
|
case BIOS_DEBUG_OBJECT_TYPE_THREAD:
|
2022-09-06 19:40:46 -04:00
|
|
|
for(auto it = std::begin(m_threads); it != std::end(m_threads); it++)
|
|
|
|
{
|
|
|
|
auto thread = *it;
|
|
|
|
if(!thread) continue;
|
2022-09-10 12:47:16 -04:00
|
|
|
|
2022-09-06 19:40:46 -04:00
|
|
|
auto threadContext = reinterpret_cast<THREADCONTEXT*>(GetStructPtr(thread->contextPtr));
|
|
|
|
|
|
|
|
uint32 pc = 0;
|
|
|
|
uint32 ra = 0;
|
|
|
|
uint32 sp = 0;
|
2022-09-10 12:47:16 -04:00
|
|
|
|
2022-09-06 19:40:46 -04:00
|
|
|
if(m_currentThreadId == it)
|
|
|
|
{
|
|
|
|
pc = m_ee.m_State.nPC;
|
|
|
|
ra = m_ee.m_State.nGPR[CMIPS::RA].nV0;
|
|
|
|
sp = m_ee.m_State.nGPR[CMIPS::SP].nV0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pc = thread->epc;
|
|
|
|
ra = threadContext->gpr[CMIPS::RA].nV0;
|
|
|
|
sp = threadContext->gpr[CMIPS::SP].nV0;
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2022-09-06 19:40:46 -04:00
|
|
|
std::string stateDescription;
|
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_RUNNING:
|
|
|
|
stateDescription = "Running";
|
|
|
|
break;
|
|
|
|
case THREAD_SLEEPING:
|
|
|
|
stateDescription = "Sleeping";
|
|
|
|
break;
|
|
|
|
case THREAD_WAITING:
|
|
|
|
stateDescription = string_format("Waiting (Semaphore: %d)", thread->semaWait);
|
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED:
|
|
|
|
stateDescription = "Suspended";
|
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED_SLEEPING:
|
|
|
|
stateDescription = "Suspended+Sleeping";
|
|
|
|
break;
|
|
|
|
case THREAD_SUSPENDED_WAITING:
|
|
|
|
stateDescription = string_format("Suspended+Waiting (Semaphore: %d)", thread->semaWait);
|
|
|
|
break;
|
|
|
|
case THREAD_ZOMBIE:
|
|
|
|
stateDescription = "Zombie";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
stateDescription = "Unknown";
|
|
|
|
break;
|
|
|
|
}
|
2015-05-06 00:54:15 -04:00
|
|
|
|
2022-09-06 19:40:46 -04:00
|
|
|
BIOS_DEBUG_OBJECT obj;
|
|
|
|
obj.fields = {
|
2022-09-10 12:47:16 -04:00
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, it),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, thread->currPriority),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, pc),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, ra),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, sp),
|
2023-07-09 11:13:25 -04:00
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, thread->threadProc),
|
2022-09-10 12:47:16 -04:00
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<std::string>, stateDescription),
|
2022-09-06 19:40:46 -04:00
|
|
|
};
|
|
|
|
result.push_back(obj);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return result;
|
2015-05-06 00:54:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|