Compare commits
7 Commits
ae68ebad87
...
21633398bb
Author | SHA1 | Date |
---|---|---|
Julio César Rocha | 21633398bb | |
zilmar | 7cb0c258a1 | |
zilmar | c098a6a464 | |
zilmar | 3340c032c3 | |
zilmar | df9b04bb5b | |
Julio C. Rocha | 4d74330673 | |
Julio C. Rocha | 628a0b2809 |
|
@ -118,6 +118,8 @@ Thumbs.db
|
|||
/Plugin64/RSP/lib
|
||||
/Plugin64/RSP/map
|
||||
/Plugin64/RSP/pdb
|
||||
/Plugin64/RSP/Project64-RSP-Basic_d.dll
|
||||
/Plugin64/RSP/Project64-RSP_d.dll
|
||||
/Plugin64/RSP/RSP 1.7.dll
|
||||
/Plugin64/RSP/RSP_d 1.7.dll
|
||||
/Plugin64/RSP/RSP-HLE_d.dll
|
||||
|
|
|
@ -279,7 +279,6 @@ copy /y "$(ProjectDir)Source\ini\GLideN64.custom.ini" "$(SolutionDir)Plugin\$(Pl
|
|||
<ClInclude Include="Source\src\Graphics\PixelBuffer.h" />
|
||||
<ClInclude Include="Source\src\Graphics\ShaderProgram.h" />
|
||||
<ClInclude Include="Source\src\gSP.h" />
|
||||
<ClInclude Include="Source\src\inc\glext.h" />
|
||||
<ClInclude Include="Source\src\Log.h" />
|
||||
<ClInclude Include="Source\src\MemoryStatus.h" />
|
||||
<ClInclude Include="Source\src\N64.h" />
|
||||
|
|
|
@ -652,9 +652,6 @@
|
|||
<ClInclude Include="Source\src\gSP.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Source\src\inc\glext.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Source\src\Log.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
#include <Project64-rsp-core/Hle/HleTask.h>
|
||||
#include <Project64-rsp-core/cpu/RSPRegisterHandlerPlugin.h>
|
||||
#include <Project64-rsp-core/cpu/RspMemory.h>
|
||||
#include <Project64-rsp-core/cpu/RspSystem.h>
|
||||
#include <zlib/zlib.h>
|
||||
|
||||
CHleTask::CHleTask(CRSPSystem & System) :
|
||||
CGPRRegisters(System.m_Reg.m_GPR),
|
||||
m_hle(System),
|
||||
m_System(System),
|
||||
m_Recompiler(System.m_Recompiler),
|
||||
m_RSPRegisterHandler(System.m_RSPRegisterHandler),
|
||||
m_MI_INTR_REG(System.m_MI_INTR_REG),
|
||||
m_SP_STATUS_REG(System.m_SP_STATUS_REG),
|
||||
m_SP_DMA_FULL_REG(System.m_SP_DMA_FULL_REG),
|
||||
m_SP_DMA_BUSY_REG(System.m_SP_DMA_BUSY_REG),
|
||||
m_SP_PC_REG(System.m_SP_PC_REG),
|
||||
m_SP_SEMAPHORE_REG(System.m_SP_SEMAPHORE_REG),
|
||||
m_DPC_STATUS_REG(System.m_DPC_STATUS_REG),
|
||||
m_DMEM(System.m_DMEM),
|
||||
m_IMEM(System.m_IMEM),
|
||||
m_UcodeCRC(0),
|
||||
CheckInterrupts(System.CheckInterrupts),
|
||||
ProcessDList(System.ProcessDList)
|
||||
{
|
||||
|
@ -30,6 +40,200 @@ bool CHleTask::IsHleTask(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
void CHleTask::SetupCommandList(TASK_INFO & TaskInfo)
|
||||
{
|
||||
uint32_t JumpTableLength = 0x7E, JumpTablePos = 0x10;
|
||||
if ((HLETaskType)(TaskInfo.Type) == HLETaskType::Audio)
|
||||
{
|
||||
if (*((uint32_t *)&m_DMEM[0]) == 0x00000001 && *((uint32_t *)&m_DMEM[0x30]) == 0xf0000f00)
|
||||
{
|
||||
JumpTableLength = 0x10;
|
||||
JumpTablePos = 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t JumpTableCRC = crc32(0L, m_IMEM + JumpTablePos, JumpTableLength << 1);
|
||||
TaskFunctionMap::iterator itr = m_FunctionMap.find(JumpTableCRC);
|
||||
if (itr != m_FunctionMap.end())
|
||||
{
|
||||
m_TaskFunctions = &itr->second;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_FunctionMap.size() > 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
m_TaskFunctions = nullptr;
|
||||
|
||||
memset(&m_Recompiler.m_CurrentBlock, 0, sizeof(m_Recompiler.m_CurrentBlock));
|
||||
m_Recompiler.BuildBranchLabels();
|
||||
TaskFunctions JumpFunctions;
|
||||
for (uint32_t i = 0, n = JumpTableLength; i < n; i++)
|
||||
{
|
||||
uint16_t FuncAddress = *((uint16_t *)(m_DMEM + (((i << 1) + JumpTablePos) ^ 2)));
|
||||
if (FuncAddress != 0x1118)
|
||||
{
|
||||
m_Recompiler.CompileHLETask(FuncAddress);
|
||||
void * FuncPtr = *(JumpTable + ((FuncAddress & 0xFFF) >> 2));
|
||||
JumpFunctions.emplace_back(TaskFunctionAddress(FuncAddress, FuncPtr));
|
||||
}
|
||||
else
|
||||
{
|
||||
JumpFunctions.emplace_back(TaskFunctionAddress(FuncAddress, nullptr));
|
||||
}
|
||||
}
|
||||
m_Recompiler.LinkBranches(&m_Recompiler.m_CurrentBlock);
|
||||
m_FunctionMap[JumpTableCRC] = JumpFunctions;
|
||||
itr = m_FunctionMap.find(JumpTableCRC);
|
||||
if (itr == m_FunctionMap.end())
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
return;
|
||||
}
|
||||
m_TaskFunctions = &itr->second;
|
||||
}
|
||||
|
||||
void CHleTask::ExecuteTask_1a13a51a(TASK_INFO & TaskInfo)
|
||||
{
|
||||
*((uint32_t *)(m_DMEM + 0x320)) = 0;
|
||||
GPR_T8 = 0x360;
|
||||
GPR_S7 = 0xF90;
|
||||
if ((*m_DPC_STATUS_REG & 1) != 0 || *m_SP_SEMAPHORE_REG != 0 || *m_SP_DMA_FULL_REG != 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
return;
|
||||
}
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_MEM_ADDR, 0x380);
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_DRAM_ADDR, TaskInfo.DataPtr);
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_RD_LEN, 0x13F);
|
||||
if (*m_SP_DMA_BUSY_REG != 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
return;
|
||||
}
|
||||
*m_SP_SEMAPHORE_REG = 0;
|
||||
if (SyncCPU)
|
||||
{
|
||||
*m_SP_PC_REG = 0x0E4;
|
||||
RSPSystem.SyncSystem()->ExecuteOps(200, *m_SP_PC_REG);
|
||||
RSPSystem.BasicSyncCheck();
|
||||
}
|
||||
|
||||
GPR_GP = TaskInfo.DataPtr;
|
||||
GPR_K1 = TaskInfo.DataSize;
|
||||
GPR_SP = 0x380;
|
||||
GPR_S8 = 0x140;
|
||||
static uint32_t TaskCount = 0;
|
||||
for (;;)
|
||||
{
|
||||
GPR_K0 = *((uint32_t *)(m_DMEM + GPR_SP));
|
||||
GPR_T9 = *((uint32_t *)(m_DMEM + GPR_SP + 4));
|
||||
|
||||
uint32_t Index = (GPR_K0 >> 0x18) & 0x7F;
|
||||
GPR_GP += 8;
|
||||
GPR_K1 -= 8;
|
||||
GPR_SP += 8;
|
||||
GPR_S8 -= 8;
|
||||
if (Index >= m_TaskFunctions->size())
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
TaskFunctionAddress FunctionAddress = (*m_TaskFunctions)[Index];
|
||||
*m_SP_PC_REG = FunctionAddress.first;
|
||||
if (SyncCPU)
|
||||
{
|
||||
RSPSystem.SyncSystem()->ExecuteOps(0x10000, 0x118);
|
||||
}
|
||||
#if defined(_M_IX86) && defined(_MSC_VER)
|
||||
void * Block = FunctionAddress.second;
|
||||
_asm {
|
||||
pushad
|
||||
call Block
|
||||
popad
|
||||
}
|
||||
#else
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
#endif
|
||||
if (SyncCPU)
|
||||
{
|
||||
RSPSystem.BasicSyncCheck();
|
||||
RSPSystem.SyncSystem()->ExecuteOps(2, (uint32_t)-1);
|
||||
}
|
||||
if (GPR_S8 == 0)
|
||||
{
|
||||
if (GPR_K1 <= 0)
|
||||
{
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_STATUS, 0x4000);
|
||||
RSPSystem.m_Op.Special_BREAK();
|
||||
if (SyncCPU)
|
||||
{
|
||||
*m_SP_PC_REG = 0x144;
|
||||
RSPSystem.SyncSystem()->ExecuteOps(100, 0x144);
|
||||
RSPSystem.BasicSyncCheck();
|
||||
}
|
||||
break;
|
||||
}
|
||||
uint32_t ReadLen = (GPR_K1 > 0x140) ? 0x140 : GPR_K1;
|
||||
GPR_S8 = ReadLen;
|
||||
if (*m_SP_SEMAPHORE_REG != 0 || *m_SP_DMA_FULL_REG != 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_MEM_ADDR, 0x380);
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_DRAM_ADDR, GPR_GP);
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_RD_LEN, ReadLen - 1);
|
||||
GPR_SP = 0x380;
|
||||
if (*m_SP_DMA_BUSY_REG != 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
*m_SP_SEMAPHORE_REG = 0;
|
||||
if (SyncCPU)
|
||||
{
|
||||
*m_SP_PC_REG = 0x0E4;
|
||||
RSPSystem.SyncSystem()->ExecuteOps(400, 0x0E4);
|
||||
RSPSystem.BasicSyncCheck();
|
||||
}
|
||||
}
|
||||
TaskCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CHleTask::SetupTask(TASK_INFO & TaskInfo)
|
||||
{
|
||||
if (TaskInfo.Flags != 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
if (*m_SP_DMA_FULL_REG != 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_MEM_ADDR, 0);
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_DRAM_ADDR, TaskInfo.UcodeData);
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_RD_LEN, TaskInfo.UcodeDataSize);
|
||||
if (*m_SP_DMA_BUSY_REG != 0 || (*m_SP_STATUS_REG & 0x80) != 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_MEM_ADDR, 0x1080);
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_DRAM_ADDR, TaskInfo.Ucode);
|
||||
m_RSPRegisterHandler->WriteReg(RSPRegister_RD_LEN, 0x0F7F);
|
||||
if (*m_SP_DMA_BUSY_REG != 0 || (*m_SP_STATUS_REG & 0x80) != 0)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
*m_SP_SEMAPHORE_REG = 0;
|
||||
if (SyncCPU)
|
||||
{
|
||||
*m_SP_PC_REG = 0x80;
|
||||
RSPSystem.SyncSystem()->ExecuteOps(200, 0x080);
|
||||
RSPSystem.BasicSyncCheck();
|
||||
}
|
||||
SetupCommandList(TaskInfo);
|
||||
}
|
||||
|
||||
bool CHleTask::ProcessHleTask(void)
|
||||
{
|
||||
TASK_INFO & TaskInfo = *((TASK_INFO *)(m_DMEM + 0xFC0));
|
||||
|
@ -37,18 +241,18 @@ bool CHleTask::ProcessHleTask(void)
|
|||
|
||||
if (((HLETaskType)TaskInfo.Type) == HLETaskType::Video && GraphicsHle && TaskInfo.DataPtr != 0)
|
||||
{
|
||||
if (ProcessDList != nullptr)
|
||||
if (ProcessDList == nullptr)
|
||||
{
|
||||
ProcessDList();
|
||||
return false;
|
||||
}
|
||||
ProcessDList();
|
||||
*m_SP_STATUS_REG |= (0x0203);
|
||||
if ((*m_SP_STATUS_REG & SP_STATUS_INTR_BREAK) != 0)
|
||||
{
|
||||
*RSPInfo.MI_INTR_REG |= MI_INTR_SP;
|
||||
RSPInfo.CheckInterrupts();
|
||||
*m_MI_INTR_REG |= MI_INTR_SP;
|
||||
CheckInterrupts();
|
||||
}
|
||||
|
||||
*RSPInfo.DPC_STATUS_REG &= ~0x0002;
|
||||
*m_DPC_STATUS_REG &= ~0x0002;
|
||||
return true;
|
||||
}
|
||||
else if (TaskInfo.Type == 7)
|
||||
|
@ -56,6 +260,30 @@ bool CHleTask::ProcessHleTask(void)
|
|||
RSPInfo.ShowCFB();
|
||||
}
|
||||
|
||||
if (CRSPSettings::CPUMethod() == RSPCpuMethod::RecompilerTasks)
|
||||
{
|
||||
if (SyncCPU)
|
||||
{
|
||||
RSPSystem.SetupSyncCPU();
|
||||
}
|
||||
SetupTask(TaskInfo);
|
||||
uint32_t UcodeSize = TaskInfo.UcodeSize;
|
||||
if (UcodeSize < 0x4 || TaskInfo.UcodeSize > 0x0F80)
|
||||
{
|
||||
UcodeSize = 0x0F80;
|
||||
}
|
||||
m_UcodeCRC = crc32(0L, m_IMEM + 0x80, UcodeSize);
|
||||
if (m_UcodeCRC == 0x1a13a51a)
|
||||
{
|
||||
ExecuteTask_1a13a51a(TaskInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CRSPSettings::CPUMethod() == RSPCpuMethod::HighLevelEmulation && m_hle.try_fast_audio_dispatching())
|
||||
{
|
||||
*m_SP_STATUS_REG |= SP_STATUS_SIG2 | SP_STATUS_BROKE | SP_STATUS_HALT;
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
#pragma once
|
||||
#include <Project64-rsp-core/Hle/hle.h>
|
||||
#include <Project64-rsp-core/cpu/RSPRegisters.h>
|
||||
#include <Project64-rsp-core/cpu/RspTypes.h>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
class CRSPSystem;
|
||||
class RSPRegisterHandlerPlugin;
|
||||
class CRSPRecompiler;
|
||||
class CHle;
|
||||
|
||||
class CHleTask
|
||||
class CHleTask :
|
||||
private CGPRRegisters
|
||||
{
|
||||
typedef std::pair<uint32_t, void *> TaskFunctionAddress;
|
||||
typedef std::vector<TaskFunctionAddress> TaskFunctions;
|
||||
typedef std::map<uint32_t, TaskFunctions> TaskFunctionMap;
|
||||
|
||||
struct TASK_INFO
|
||||
{
|
||||
uint32_t Type;
|
||||
|
@ -46,14 +55,27 @@ private:
|
|||
CHleTask(const CHleTask & copy);
|
||||
CHleTask & operator=(const CHleTask & rhs);
|
||||
|
||||
void SetupCommandList(TASK_INFO & TaskInfo);
|
||||
void ExecuteTask_1a13a51a(TASK_INFO & TaskInfo);
|
||||
void SetupTask(TASK_INFO & TaskInfo);
|
||||
|
||||
void (*&CheckInterrupts)(void);
|
||||
void (*&ProcessDList)(void);
|
||||
|
||||
CHle m_hle;
|
||||
TaskFunctionMap m_FunctionMap;
|
||||
const TaskFunctions * m_TaskFunctions;
|
||||
CRSPSystem & m_System;
|
||||
CRSPRecompiler & m_Recompiler;
|
||||
RSPRegisterHandlerPlugin *& m_RSPRegisterHandler;
|
||||
uint32_t *& m_MI_INTR_REG;
|
||||
uint32_t *& m_SP_STATUS_REG;
|
||||
uint32_t *& m_SP_DMA_FULL_REG;
|
||||
uint32_t *& m_SP_DMA_BUSY_REG;
|
||||
uint32_t *& m_SP_PC_REG;
|
||||
uint32_t *& m_SP_SEMAPHORE_REG;
|
||||
uint32_t *& m_DPC_STATUS_REG;
|
||||
uint8_t *& m_DMEM;
|
||||
uint8_t *& m_IMEM;
|
||||
uint32_t m_UcodeCRC;
|
||||
};
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include <Project64-rsp-core/cpu/RspSystem.h>
|
||||
#include <Settings/Settings.h>
|
||||
|
||||
// https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
|
||||
#include <intrin.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#define X86_RECOMP_VERBOSE
|
||||
#define BUILD_BRANCHLABELS_VERBOSE
|
||||
|
||||
uint32_t CompilePC, JumpTableSize, BlockID = 0;
|
||||
uint32_t JumpTableSize, BlockID = 0;
|
||||
uint32_t dwBuffer = MainBuffer;
|
||||
bool ChangedPC;
|
||||
|
||||
|
@ -42,7 +42,9 @@ p_Recompfunc RSP_Recomp_Sc2[32];
|
|||
|
||||
CRSPRecompiler::CRSPRecompiler(CRSPSystem & System) :
|
||||
m_System(System),
|
||||
m_RecompilerOps(System, *this),
|
||||
m_RSPRegisterHandler(System.m_RSPRegisterHandler),
|
||||
m_CompilePC(0),
|
||||
m_OpCode(System.m_OpCode),
|
||||
m_NextInstruction(RSPPIPELINE_NORMAL),
|
||||
m_IMEM(System.m_IMEM)
|
||||
|
@ -717,7 +719,7 @@ sections as well as set the jump table to points
|
|||
within a block that are safe.
|
||||
*/
|
||||
|
||||
void BuildBranchLabels(void)
|
||||
void CRSPRecompiler::BuildBranchLabels(void)
|
||||
{
|
||||
RSPOpcode RspOp;
|
||||
uint32_t i, Dest;
|
||||
|
@ -797,7 +799,7 @@ bool IsJumpLabel(uint32_t PC)
|
|||
|
||||
void CRSPRecompiler::CompilerLinkBlocks(void)
|
||||
{
|
||||
uint8_t * KnownCode = (uint8_t *)*(JumpTable + (CompilePC >> 2));
|
||||
uint8_t * KnownCode = (uint8_t *)*(JumpTable + (m_CompilePC >> 2));
|
||||
|
||||
CPU_Message("***** Linking block to X86: %08X *****", KnownCode);
|
||||
m_NextInstruction = RSPPIPELINE_FINISH_BLOCK;
|
||||
|
@ -809,16 +811,14 @@ void CRSPRecompiler::CompilerLinkBlocks(void)
|
|||
|
||||
void CRSPRecompiler::CompilerRSPBlock(void)
|
||||
{
|
||||
CRSPRecompilerOps RecompilerOps(RSPSystem, *this);
|
||||
|
||||
uint8_t * IMEM_SAVE = (uint8_t *)malloc(0x1000);
|
||||
const size_t X86BaseAddress = (size_t)RecompPos;
|
||||
m_NextInstruction = RSPPIPELINE_NORMAL;
|
||||
CompilePC = *m_System.m_SP_PC_REG;
|
||||
m_CompilePC = *m_System.m_SP_PC_REG;
|
||||
|
||||
memset(&m_CurrentBlock, 0, sizeof(m_CurrentBlock));
|
||||
m_CurrentBlock.StartPC = CompilePC;
|
||||
m_CurrentBlock.CurrPC = CompilePC;
|
||||
m_CurrentBlock.StartPC = m_CompilePC;
|
||||
m_CurrentBlock.CurrPC = m_CompilePC;
|
||||
|
||||
// Align the block to a boundary
|
||||
if (X86BaseAddress & 7)
|
||||
|
@ -846,7 +846,7 @@ void CRSPRecompiler::CompilerRSPBlock(void)
|
|||
}
|
||||
|
||||
// This is for the block about to be compiled
|
||||
*(JumpTable + (CompilePC >> 2)) = RecompPos;
|
||||
*(JumpTable + (m_CompilePC >> 2)) = RecompPos;
|
||||
|
||||
uint32_t EndPC = 0x1000;
|
||||
do
|
||||
|
@ -854,17 +854,17 @@ void CRSPRecompiler::CompilerRSPBlock(void)
|
|||
// Reordering is setup to allow us to have loop labels
|
||||
// so here we see if this is one and put it in the jump table
|
||||
|
||||
if (m_NextInstruction == RSPPIPELINE_NORMAL && IsJumpLabel(CompilePC))
|
||||
if (m_NextInstruction == RSPPIPELINE_NORMAL && IsJumpLabel(m_CompilePC))
|
||||
{
|
||||
// Jumps come around twice
|
||||
if (NULL == *(JumpTable + (CompilePC >> 2)))
|
||||
if (NULL == *(JumpTable + (m_CompilePC >> 2)))
|
||||
{
|
||||
CPU_Message("***** Adding jump table entry for PC: %04X at X86: %08X *****", CompilePC, RecompPos);
|
||||
CPU_Message("***** Adding jump table entry for PC: %04X at X86: %08X *****", m_CompilePC, RecompPos);
|
||||
CPU_Message("");
|
||||
*(JumpTable + (CompilePC >> 2)) = RecompPos;
|
||||
*(JumpTable + (m_CompilePC >> 2)) = RecompPos;
|
||||
|
||||
// Reorder from here to next label or branch
|
||||
m_CurrentBlock.CurrPC = CompilePC;
|
||||
m_CurrentBlock.CurrPC = m_CompilePC;
|
||||
ReOrderSubBlock(&m_CurrentBlock);
|
||||
}
|
||||
else if (m_NextInstruction != RSPPIPELINE_DELAY_SLOT_DONE)
|
||||
|
@ -876,19 +876,19 @@ void CRSPRecompiler::CompilerRSPBlock(void)
|
|||
|
||||
if (Compiler.bSections)
|
||||
{
|
||||
if (RecompilerOps.RSP_DoSections())
|
||||
if (m_RecompilerOps.RSP_DoSections())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef X86_RECOMP_VERBOSE
|
||||
if (!IsOpcodeNop(CompilePC))
|
||||
if (!IsOpcodeNop(m_CompilePC))
|
||||
{
|
||||
CPU_Message("X86 Address: %08X", RecompPos);
|
||||
}
|
||||
#endif
|
||||
RSP_LW_IMEM(CompilePC, &m_OpCode.Value);
|
||||
RSP_LW_IMEM(m_CompilePC, &m_OpCode.Value);
|
||||
|
||||
if (m_OpCode.Value == 0xFFFFFFFF)
|
||||
{
|
||||
|
@ -897,40 +897,48 @@ void CRSPRecompiler::CompilerRSPBlock(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
(RecompilerOps.*RSP_Recomp_Opcode[m_OpCode.op])();
|
||||
(m_RecompilerOps.*RSP_Recomp_Opcode[m_OpCode.op])();
|
||||
}
|
||||
|
||||
switch (m_NextInstruction)
|
||||
{
|
||||
case RSPPIPELINE_NORMAL:
|
||||
CompilePC += 4;
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
case RSPPIPELINE_DO_DELAY_SLOT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT;
|
||||
CompilePC += 4;
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
case RSPPIPELINE_DO_DELAY_SLOT_TASK_EXIT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_TASK_EXIT;
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
case RSPPIPELINE_DELAY_SLOT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_DONE;
|
||||
CompilePC = (CompilePC - 4 & 0xFFC);
|
||||
m_CompilePC = (m_CompilePC - 4 & 0xFFC);
|
||||
break;
|
||||
case RSPPIPELINE_DELAY_SLOT_EXIT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_EXIT_DONE;
|
||||
CompilePC = (CompilePC - 4 & 0xFFC);
|
||||
m_CompilePC = (m_CompilePC - 4 & 0xFFC);
|
||||
break;
|
||||
case RSPPIPELINE_DELAY_SLOT_TASK_EXIT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_TASK_EXIT_DONE;
|
||||
m_CompilePC = (m_CompilePC - 4 & 0xFFC);
|
||||
break;
|
||||
case RSPPIPELINE_FINISH_SUB_BLOCK:
|
||||
m_NextInstruction = RSPPIPELINE_NORMAL;
|
||||
CompilePC += 8;
|
||||
if (CompilePC >= 0x1000)
|
||||
m_CompilePC += 8;
|
||||
if (m_CompilePC >= 0x1000)
|
||||
{
|
||||
m_NextInstruction = RSPPIPELINE_FINISH_BLOCK;
|
||||
}
|
||||
else if (NULL == *(JumpTable + (CompilePC >> 2)))
|
||||
else if (NULL == *(JumpTable + (m_CompilePC >> 2)))
|
||||
{
|
||||
// This is for the new block being compiled now
|
||||
CPU_Message("***** Continuing static SubBlock (jump table entry added for PC: %04X at X86: %08X) *****", CompilePC, RecompPos);
|
||||
*(JumpTable + (CompilePC >> 2)) = RecompPos;
|
||||
CPU_Message("***** Continuing static SubBlock (jump table entry added for PC: %04X at X86: %08X) *****", m_CompilePC, RecompPos);
|
||||
*(JumpTable + (m_CompilePC >> 2)) = RecompPos;
|
||||
|
||||
m_CurrentBlock.CurrPC = CompilePC;
|
||||
m_CurrentBlock.CurrPC = m_CompilePC;
|
||||
// Reorder from after delay to next label or branch
|
||||
ReOrderSubBlock(&m_CurrentBlock);
|
||||
}
|
||||
|
@ -939,23 +947,25 @@ void CRSPRecompiler::CompilerRSPBlock(void)
|
|||
CompilerLinkBlocks();
|
||||
}
|
||||
break;
|
||||
|
||||
case RSPPIPELINE_FINISH_TASK_SUB_BLOCK:
|
||||
m_NextInstruction = RSPPIPELINE_FINISH_BLOCK;
|
||||
break;
|
||||
case RSPPIPELINE_FINISH_BLOCK: break;
|
||||
default:
|
||||
g_Notify->DisplayError(stdstr_f("RSP main loop\n\nWTF m_NextInstruction = %d", m_NextInstruction).c_str());
|
||||
CompilePC += 4;
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (CompilePC >= EndPC && *m_System.m_SP_PC_REG != 0 && EndPC != *m_System.m_SP_PC_REG)
|
||||
if (m_CompilePC >= EndPC && *m_System.m_SP_PC_REG != 0 && EndPC != *m_System.m_SP_PC_REG)
|
||||
{
|
||||
CompilePC = 0;
|
||||
m_CompilePC = 0;
|
||||
EndPC = *m_System.m_SP_PC_REG;
|
||||
}
|
||||
} while (m_NextInstruction != RSPPIPELINE_FINISH_BLOCK && (CompilePC < EndPC || m_NextInstruction == RSPPIPELINE_DELAY_SLOT || m_NextInstruction == RSPPIPELINE_DELAY_SLOT_DONE));
|
||||
if (CompilePC >= EndPC)
|
||||
} while (m_NextInstruction != RSPPIPELINE_FINISH_BLOCK && (m_CompilePC < EndPC || m_NextInstruction == RSPPIPELINE_DELAY_SLOT || m_NextInstruction == RSPPIPELINE_DELAY_SLOT_DONE));
|
||||
if (m_CompilePC >= EndPC)
|
||||
{
|
||||
MoveConstToVariable((CompilePC & 0xFFC), m_System.m_SP_PC_REG, "RSP PC");
|
||||
MoveConstToVariable((m_CompilePC & 0xFFC), m_System.m_SP_PC_REG, "RSP PC");
|
||||
Ret();
|
||||
}
|
||||
CPU_Message("===== End of recompiled code =====");
|
||||
|
@ -1000,7 +1010,7 @@ void CRSPRecompiler::RunCPU(void)
|
|||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
char ErrorMessage[400];
|
||||
sprintf(ErrorMessage, "Error CompilePC = %08X", CompilePC);
|
||||
sprintf(ErrorMessage, "Error m_CompilePC = %08X", m_CompilePC);
|
||||
g_Notify->DisplayError(ErrorMessage);
|
||||
ClearAllx86Code();
|
||||
continue;
|
||||
|
@ -1058,6 +1068,126 @@ void CRSPRecompiler::RunCPU(void)
|
|||
}
|
||||
}
|
||||
|
||||
void CRSPRecompiler::CompileHLETask(uint32_t Address)
|
||||
{
|
||||
uint32_t EndPC = 0x1000;
|
||||
m_CompilePC = (Address & 0xFFF);
|
||||
m_NextInstruction = RSPPIPELINE_NORMAL;
|
||||
|
||||
m_CurrentBlock.StartPC = Address;
|
||||
m_CurrentBlock.CurrPC = Address;
|
||||
|
||||
CPU_Message("====== Block %d ======", BlockID++);
|
||||
CPU_Message("x86 code at: %X", RecompPos);
|
||||
CPU_Message("Jump table: %X", Table);
|
||||
CPU_Message("Start of block: %X", m_CurrentBlock.StartPC);
|
||||
CPU_Message("====== Recompiled code ======");
|
||||
|
||||
*(JumpTable + (m_CompilePC >> 2)) = RecompPos;
|
||||
do
|
||||
{
|
||||
if (m_NextInstruction == RSPPIPELINE_NORMAL && IsJumpLabel(m_CompilePC))
|
||||
{
|
||||
// Jumps come around twice
|
||||
if (NULL == *(JumpTable + (m_CompilePC >> 2)))
|
||||
{
|
||||
CPU_Message("***** Adding jump table entry for PC: %04X at X86: %08X *****", m_CompilePC, RecompPos);
|
||||
CPU_Message("");
|
||||
*(JumpTable + (m_CompilePC >> 2)) = RecompPos;
|
||||
|
||||
// Reorder from here to next label or branch
|
||||
m_CurrentBlock.CurrPC = m_CompilePC;
|
||||
ReOrderSubBlock(&m_CurrentBlock);
|
||||
}
|
||||
else if (m_NextInstruction != RSPPIPELINE_DELAY_SLOT_DONE)
|
||||
{
|
||||
|
||||
// We could link the blocks here, but performance-wise it might be better to just let it run
|
||||
}
|
||||
}
|
||||
#ifdef X86_RECOMP_VERBOSE
|
||||
if (!IsOpcodeNop(m_CompilePC))
|
||||
{
|
||||
CPU_Message("X86 Address: %08X", RecompPos);
|
||||
}
|
||||
#endif
|
||||
RSP_LW_IMEM(m_CompilePC, &m_OpCode.Value);
|
||||
(m_RecompilerOps.*RSP_Recomp_Opcode[m_OpCode.op])();
|
||||
|
||||
switch (m_NextInstruction)
|
||||
{
|
||||
case RSPPIPELINE_NORMAL:
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
case RSPPIPELINE_DO_DELAY_SLOT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT;
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
case RSPPIPELINE_DO_DELAY_SLOT_EXIT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_EXIT;
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
case RSPPIPELINE_DO_DELAY_SLOT_TASK_EXIT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_TASK_EXIT;
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
case RSPPIPELINE_DELAY_SLOT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_DONE;
|
||||
m_CompilePC = (m_CompilePC - 4 & 0xFFC);
|
||||
break;
|
||||
case RSPPIPELINE_DELAY_SLOT_EXIT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_EXIT_DONE;
|
||||
m_CompilePC = (m_CompilePC - 4 & 0xFFC);
|
||||
break;
|
||||
case RSPPIPELINE_DELAY_SLOT_TASK_EXIT:
|
||||
m_NextInstruction = RSPPIPELINE_DELAY_SLOT_TASK_EXIT_DONE;
|
||||
m_CompilePC = (m_CompilePC - 4 & 0xFFC);
|
||||
break;
|
||||
case RSPPIPELINE_FINISH_SUB_BLOCK:
|
||||
m_NextInstruction = RSPPIPELINE_NORMAL;
|
||||
m_CompilePC += 8;
|
||||
if (m_CompilePC >= 0x1000)
|
||||
{
|
||||
m_NextInstruction = RSPPIPELINE_FINISH_BLOCK;
|
||||
}
|
||||
else if (NULL == *(JumpTable + (m_CompilePC >> 2)))
|
||||
{
|
||||
// This is for the new block being compiled now
|
||||
CPU_Message("***** Continuing static SubBlock (jump table entry added for PC: %04X at X86: %08X) *****", m_CompilePC, RecompPos);
|
||||
*(JumpTable + (m_CompilePC >> 2)) = RecompPos;
|
||||
|
||||
m_CurrentBlock.CurrPC = m_CompilePC;
|
||||
// Reorder from after delay to next label or branch
|
||||
ReOrderSubBlock(&m_CurrentBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
CompilerLinkBlocks();
|
||||
}
|
||||
break;
|
||||
case RSPPIPELINE_FINISH_BLOCK: break;
|
||||
case RSPPIPELINE_FINISH_TASK_SUB_BLOCK:
|
||||
m_NextInstruction = RSPPIPELINE_FINISH_BLOCK;
|
||||
break;
|
||||
default:
|
||||
g_Notify->DisplayError(stdstr_f("RSP main loop\n\nWTF m_NextInstruction = %d", m_NextInstruction).c_str());
|
||||
m_CompilePC += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_CompilePC >= EndPC && *m_System.m_SP_PC_REG != 0 && EndPC != *m_System.m_SP_PC_REG)
|
||||
{
|
||||
m_CompilePC = 0;
|
||||
EndPC = *m_System.m_SP_PC_REG;
|
||||
}
|
||||
} while (m_NextInstruction != RSPPIPELINE_FINISH_BLOCK && (m_CompilePC < EndPC || m_NextInstruction == RSPPIPELINE_DELAY_SLOT || m_NextInstruction == RSPPIPELINE_DELAY_SLOT_DONE));
|
||||
if (m_CompilePC >= EndPC)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
CPU_Message("===== End of recompiled code =====");
|
||||
}
|
||||
|
||||
void CRSPRecompiler::Branch_AddRef(uint32_t Target, uint32_t * X86Loc)
|
||||
{
|
||||
if (m_CurrentBlock.ResolveCount >= 150)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Project64-rsp-core/Recompiler/RspRecompilerOps.h>
|
||||
#include <Project64-rsp-core/Settings/RspSettings.h>
|
||||
#include <Project64-rsp-core/cpu/RSPOpcode.h>
|
||||
#include <Project64-rsp-core/cpu/RspPipelineStage.h>
|
||||
|
@ -12,6 +13,7 @@ class RSPRegisterHandlerPlugin;
|
|||
class CRSPRecompiler
|
||||
{
|
||||
friend class CRSPRecompilerOps;
|
||||
friend class CHleTask;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -38,23 +40,27 @@ private:
|
|||
CRSPRecompiler(const CRSPRecompiler &);
|
||||
CRSPRecompiler & operator=(const CRSPRecompiler &);
|
||||
|
||||
void BuildBranchLabels(void);
|
||||
void BuildRecompilerCPU(void);
|
||||
void CompilerLinkBlocks(void);
|
||||
void CompilerRSPBlock(void);
|
||||
void CompileHLETask(uint32_t Address);
|
||||
void LinkBranches(RSP_BLOCK * Block);
|
||||
void ReOrderSubBlock(RSP_BLOCK * Block);
|
||||
void ReOrderInstructions(uint32_t StartPC, uint32_t EndPC);
|
||||
void ResetJumpTables(void);
|
||||
|
||||
CRSPSystem & m_System;
|
||||
CRSPRecompilerOps m_RecompilerOps;
|
||||
RSPRegisterHandlerPlugin *& m_RSPRegisterHandler;
|
||||
RSPOpcode & m_OpCode;
|
||||
uint32_t m_CompilePC;
|
||||
RSP_BLOCK m_CurrentBlock;
|
||||
RSPPIPELINE_STAGE m_NextInstruction;
|
||||
uint8_t *& m_IMEM;
|
||||
};
|
||||
|
||||
extern uint32_t CompilePC, JumpTableSize;
|
||||
extern uint32_t JumpTableSize;
|
||||
extern bool ChangedPC;
|
||||
|
||||
#define CompilerWarning \
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,8 +1,9 @@
|
|||
// Opcode functions
|
||||
#pragma once
|
||||
#include <Project64-rsp-core/cpu/RSPInterpreterOps.h>
|
||||
|
||||
class CRSPSystem;
|
||||
class CRSPRegisters;
|
||||
class CRSPRecompiler;
|
||||
|
||||
class CRSPRecompilerOps
|
||||
{
|
||||
|
@ -11,6 +12,11 @@ class CRSPRecompilerOps
|
|||
HIT_BRANCH = 0x2,
|
||||
};
|
||||
|
||||
enum EndHleTaskOp
|
||||
{
|
||||
J_0x1118 = 0x09000446
|
||||
};
|
||||
|
||||
public:
|
||||
CRSPRecompilerOps(CRSPSystem & System, CRSPRecompiler & Recompiler);
|
||||
|
||||
|
@ -219,6 +225,7 @@ private:
|
|||
CRSPRecompiler & m_Recompiler;
|
||||
RSPPIPELINE_STAGE & m_NextInstruction;
|
||||
RSPOpcode & m_OpCode;
|
||||
uint32_t & m_CompilePC;
|
||||
UWORD32 * m_GPR;
|
||||
UDWORD * m_ACCUM;
|
||||
UWORD32 * m_Flags;
|
||||
|
|
|
@ -553,7 +553,7 @@ bool CRSPRecompilerOps::Check_Section_000(void)
|
|||
uint32_t i;
|
||||
RSPOpcode op0, op1;
|
||||
|
||||
RSP_LW_IMEM(CompilePC + 0x00, &op0.Value);
|
||||
RSP_LW_IMEM(m_CompilePC + 0x00, &op0.Value);
|
||||
|
||||
// Example: (Mario audio microcode)
|
||||
// 0x574 VMUDN $v30, $v3, $v23
|
||||
|
@ -567,7 +567,7 @@ bool CRSPRecompilerOps::Check_Section_000(void)
|
|||
|
||||
for (i = 0; i < 0x20; i++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC + 0x04 + (i * 4), &op1.Value);
|
||||
RSP_LW_IMEM(m_CompilePC + 0x04 + (i * 4), &op1.Value);
|
||||
|
||||
if (!(op1.op == RSP_CP2 && (op1.rs & 0x10) != 0 && op1.funct == RSP_VECTOR_VMADN))
|
||||
{
|
||||
|
@ -591,7 +591,7 @@ bool CRSPRecompilerOps::Check_Section_000(void)
|
|||
}
|
||||
|
||||
// TODO: check destination and flushes
|
||||
if (true == WriteToAccum(7, CompilePC + 0x4 + (Section_000_VMADN * 4) - 0x4))
|
||||
if (true == WriteToAccum(7, m_CompilePC + 0x4 + (Section_000_VMADN * 4) - 0x4))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -608,26 +608,26 @@ void CRSPRecompilerOps::Compile_Section_000(void)
|
|||
RSPOpcode vmudn, vmadn = {0};
|
||||
uint32_t i;
|
||||
|
||||
RSP_LW_IMEM(CompilePC + 0x00, &vmudn.Value);
|
||||
RSP_LW_IMEM(m_CompilePC + 0x00, &vmudn.Value);
|
||||
|
||||
CPU_Message("Compiling: %X to ..., RSP optimization $000", CompilePC);
|
||||
CPU_Message(" %X %s", CompilePC + 0x00, RSPInstruction(CompilePC + 0x00, vmudn.Value).NameAndParam().c_str());
|
||||
CPU_Message("Compiling: %X to ..., RSP optimization $000", m_CompilePC);
|
||||
CPU_Message(" %X %s", m_CompilePC + 0x00, RSPInstruction(m_CompilePC + 0x00, vmudn.Value).NameAndParam().c_str());
|
||||
|
||||
for (i = 0; i < Section_000_VMADN; i++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC + 0x04 + (i * 4), &vmadn.Value);
|
||||
CPU_Message(" %X %s", CompilePC + 0x04 + (i * 4), RSPInstruction(CompilePC + 0x04 + (i * 4), vmadn.Value).NameAndParam().c_str());
|
||||
RSP_LW_IMEM(m_CompilePC + 0x04 + (i * 4), &vmadn.Value);
|
||||
CPU_Message(" %X %s", m_CompilePC + 0x04 + (i * 4), RSPInstruction(m_CompilePC + 0x04 + (i * 4), vmadn.Value).NameAndParam().c_str());
|
||||
}
|
||||
|
||||
RSP_Sections_VMUDN(vmudn, Low16BitAccum);
|
||||
CompilePC += 4;
|
||||
m_CompilePC += 4;
|
||||
|
||||
for (i = 0; i < Section_000_VMADN; i++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC, &vmadn.Value);
|
||||
CompilePC += 4;
|
||||
RSP_LW_IMEM(m_CompilePC, &vmadn.Value);
|
||||
m_CompilePC += 4;
|
||||
RSP_Sections_VMADN(vmadn, Low16BitAccum);
|
||||
if (WriteToVectorDest(vmadn.sa, CompilePC - 4) == true)
|
||||
if (WriteToVectorDest(vmadn.sa, m_CompilePC - 4) == true)
|
||||
{
|
||||
sprintf(Reg, "m_Vect[%i].HW[0]", vmadn.sa);
|
||||
MmxMoveQwordRegToVariable(x86_MM0, &m_Vect[vmadn.sa].s16(0), Reg);
|
||||
|
@ -651,7 +651,7 @@ bool CRSPRecompilerOps::Check_Section_001(void)
|
|||
uint32_t i;
|
||||
RSPOpcode op0, op1;
|
||||
|
||||
RSP_LW_IMEM(CompilePC + 0x00, &op0.Value);
|
||||
RSP_LW_IMEM(m_CompilePC + 0x00, &op0.Value);
|
||||
|
||||
// Example: (Mario audio microcode)
|
||||
// 0xCC0 VMULF $v28, $v28, $v10 [6]
|
||||
|
@ -665,7 +665,7 @@ bool CRSPRecompilerOps::Check_Section_001(void)
|
|||
|
||||
for (i = 0; i < 0x20; i++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC + 0x04 + (i * 4), &op1.Value);
|
||||
RSP_LW_IMEM(m_CompilePC + 0x04 + (i * 4), &op1.Value);
|
||||
|
||||
if (!(op1.op == RSP_CP2 && (op1.rs & 0x10) != 0 && op1.funct == RSP_VECTOR_VMACF))
|
||||
{
|
||||
|
@ -694,7 +694,7 @@ bool CRSPRecompilerOps::Check_Section_001(void)
|
|||
}
|
||||
|
||||
// Destinations are checked elsewhere, this is fine
|
||||
if (true == WriteToAccum(7, CompilePC + 0x4 + (Section_001_VMACF * 4) - 0x4))
|
||||
if (true == WriteToAccum(7, m_CompilePC + 0x4 + (Section_001_VMACF * 4) - 0x4))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -708,35 +708,35 @@ void CRSPRecompilerOps::Compile_Section_001(void)
|
|||
char Reg[256];
|
||||
RSPOpcode vmulf, vmacf;
|
||||
|
||||
RSP_LW_IMEM(CompilePC + 0x00, &vmulf.Value);
|
||||
RSP_LW_IMEM(m_CompilePC + 0x00, &vmulf.Value);
|
||||
|
||||
CPU_Message("Compiling: %X to ..., RSP optimization $001", CompilePC);
|
||||
CPU_Message(" %X %s", CompilePC + 0x00, RSPInstruction(CompilePC + 0x00, vmulf.Value).NameAndParam().c_str());
|
||||
CPU_Message("Compiling: %X to ..., RSP optimization $001", m_CompilePC);
|
||||
CPU_Message(" %X %s", m_CompilePC + 0x00, RSPInstruction(m_CompilePC + 0x00, vmulf.Value).NameAndParam().c_str());
|
||||
|
||||
for (i = 0; i < Section_001_VMACF; i++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC + 0x04 + (i * 4), &vmacf.Value);
|
||||
CPU_Message(" %X %s", CompilePC + 0x04 + (i * 4), RSPInstruction(CompilePC + 0x04 + (i * 4), vmacf.Value).NameAndParam().c_str());
|
||||
RSP_LW_IMEM(m_CompilePC + 0x04 + (i * 4), &vmacf.Value);
|
||||
CPU_Message(" %X %s", m_CompilePC + 0x04 + (i * 4), RSPInstruction(m_CompilePC + 0x04 + (i * 4), vmacf.Value).NameAndParam().c_str());
|
||||
}
|
||||
|
||||
RSP_Sections_VMULF(vmulf, Middle16BitAccum);
|
||||
|
||||
if (WriteToVectorDest(vmulf.sa, CompilePC) == true)
|
||||
if (WriteToVectorDest(vmulf.sa, m_CompilePC) == true)
|
||||
{
|
||||
sprintf(Reg, "m_Vect[%i].HW[0]", vmulf.sa);
|
||||
MmxMoveQwordRegToVariable(x86_MM0, &m_Vect[vmulf.sa].s16(0), Reg);
|
||||
sprintf(Reg, "m_Vect[%i].HW[4]", vmulf.sa);
|
||||
MmxMoveQwordRegToVariable(x86_MM1, &m_Vect[vmulf.sa].s16(4), Reg);
|
||||
}
|
||||
CompilePC += 4;
|
||||
m_CompilePC += 4;
|
||||
|
||||
for (i = 0; i < Section_001_VMACF; i++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC, &vmacf.Value);
|
||||
CompilePC += 4;
|
||||
RSP_LW_IMEM(m_CompilePC, &vmacf.Value);
|
||||
m_CompilePC += 4;
|
||||
|
||||
RSP_Sections_VMACF(vmacf, Middle16BitAccum);
|
||||
if (WriteToVectorDest(vmacf.sa, CompilePC - 4) == true)
|
||||
if (WriteToVectorDest(vmacf.sa, m_CompilePC - 4) == true)
|
||||
{
|
||||
sprintf(Reg, "m_Vect[%i].HW[0]", vmacf.sa);
|
||||
MmxMoveQwordRegToVariable(x86_MM0, &m_Vect[vmacf.sa].s16(0), Reg);
|
||||
|
@ -755,7 +755,7 @@ bool CRSPRecompilerOps::Check_Section_002(void)
|
|||
|
||||
for (Count = 0; Count < 0x0C; Count++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC + (Count * 0x04), &op[Count].Value);
|
||||
RSP_LW_IMEM(m_CompilePC + (Count * 0x04), &op[Count].Value);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -812,7 +812,7 @@ bool CRSPRecompilerOps::Check_Section_002(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (true == WriteToAccum(7, CompilePC + 0x2C))
|
||||
if (true == WriteToAccum(7, m_CompilePC + 0x2C))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -827,11 +827,11 @@ void CRSPRecompilerOps::Compile_Section_002(void)
|
|||
|
||||
RSPOpcode vmudh, vsaw;
|
||||
|
||||
CPU_Message("Compiling: %X to ..., RSP optimization $002", CompilePC);
|
||||
CPU_Message("Compiling: %X to ..., RSP optimization $002", m_CompilePC);
|
||||
for (Count = 0; Count < 0xC; Count++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC + (Count * 0x04), &op[Count].Value);
|
||||
CPU_Message(" %X %s", CompilePC + (Count * 0x04), RSPInstruction(CompilePC + (Count * 0x04), op[Count].Value).NameAndParam().c_str());
|
||||
RSP_LW_IMEM(m_CompilePC + (Count * 0x04), &op[Count].Value);
|
||||
CPU_Message(" %X %s", m_CompilePC + (Count * 0x04), RSPInstruction(m_CompilePC + (Count * 0x04), op[Count].Value).NameAndParam().c_str());
|
||||
}
|
||||
|
||||
vmudh = op[0];
|
||||
|
@ -859,7 +859,7 @@ void CRSPRecompilerOps::Compile_Section_002(void)
|
|||
|
||||
MmxEmptyMultimediaState();
|
||||
|
||||
CompilePC += 12 * sizeof(RSPOpcode);
|
||||
m_CompilePC += 12 * sizeof(RSPOpcode);
|
||||
}
|
||||
|
||||
bool CRSPRecompilerOps::Check_Section_003(void)
|
||||
|
@ -869,7 +869,7 @@ bool CRSPRecompilerOps::Check_Section_003(void)
|
|||
|
||||
for (Count = 0; Count < 4; Count++)
|
||||
{
|
||||
RSP_LW_IMEM(CompilePC + (Count * 0x04), &op[Count].Value);
|
||||
RSP_LW_IMEM(m_CompilePC + (Count * 0x04), &op[Count].Value);
|
||||
}
|
||||
|
||||
// Example: (Zelda audio microcode)
|
||||
|
@ -880,7 +880,7 @@ bool CRSPRecompilerOps::Check_Section_003(void)
|
|||
|
||||
if (op[0].Value == 0x4BF7FDC5 && op[1].Value == 0x4BF6FDCF && op[2].Value == 0x4B92CD8D && op[3].Value == 0x4B1EFDCE)
|
||||
{
|
||||
if (true == WriteToAccum(7, CompilePC + 0xc))
|
||||
if (true == WriteToAccum(7, m_CompilePC + 0xc))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -937,10 +937,10 @@ void CRSPRecompilerOps::resampler_hle()
|
|||
|
||||
void CRSPRecompilerOps::Compile_Section_003(void)
|
||||
{
|
||||
CPU_Message("Compiling: %X to ..., RSP optimization $003", CompilePC);
|
||||
CPU_Message("Compiling: %X to ..., RSP optimization $003", m_CompilePC);
|
||||
MoveConstToX86reg((uint32_t)this, x86_ECX);
|
||||
Call_Direct(AddressOf(&CRSPRecompilerOps::resampler_hle), "Resampler_HLE");
|
||||
CompilePC += 4 * sizeof(RSPOpcode);
|
||||
m_CompilePC += 4 * sizeof(RSPOpcode);
|
||||
}
|
||||
|
||||
bool CRSPRecompilerOps::RSP_DoSections(void)
|
||||
|
|
|
@ -3333,6 +3333,28 @@ void XorX86RegToVariable(void * Variable, const char * VariableName, int x86reg)
|
|||
PUTDSTPTR(RecompPos, Variable);
|
||||
}
|
||||
|
||||
void x86_SetBranch8b(void * JumpByte, void * Destination)
|
||||
{
|
||||
// Calculate 32-bit relative offset
|
||||
size_t n = (uint8_t *)Destination - ((uint8_t *)JumpByte + 1);
|
||||
intptr_t signed_n = (intptr_t)n;
|
||||
|
||||
// Check limits, no pun intended
|
||||
if (signed_n > +128 || signed_n < -127)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
else
|
||||
{
|
||||
*(uint8_t *)(JumpByte) = (uint8_t)(n & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
void x86_SetBranch32b(void * JumpByte, void * Destination)
|
||||
{
|
||||
*(uint32_t *)(JumpByte) = (uint32_t)((uint8_t *)Destination - (uint8_t *)((uint32_t *)JumpByte + 1));
|
||||
}
|
||||
|
||||
void * GetAddressOf_(int value, ...)
|
||||
{
|
||||
void * Address;
|
||||
|
|
|
@ -14,7 +14,7 @@ RSPCpuMethod CRSPSettings::m_CPUMethod = RSPCpuMethod::Interpreter;
|
|||
#endif
|
||||
|
||||
uint16_t Set_AudioHle = 0, Set_GraphicsHle = 0, Set_MultiThreaded = 0, Set_AllocatedRdramSize = 0, Set_DirectoryLog = 0;
|
||||
bool GraphicsHle = true, AudioHle, ConditionalMove, HleAlistTask = false, RspMultiThreaded = false;
|
||||
bool GraphicsHle = true, AudioHle, ConditionalMove, SyncCPU = false, HleAlistTask = false, RspMultiThreaded = false;
|
||||
bool DebuggingEnabled = false, Profiling, IndvidualBlock, ShowErrors, BreakOnStart = false, LogRDP = false, LogX86Code = false;
|
||||
|
||||
void CRSPSettings::EnableDebugging(bool Enabled)
|
||||
|
@ -40,6 +40,7 @@ void CRSPSettings::InitializeRspSetting(void)
|
|||
RegisterSetting(Set_IndvidualBlock, Data_DWORD_General, "Individual Block", NULL, IndvidualBlock, NULL);
|
||||
RegisterSetting(Set_ShowErrors, Data_DWORD_General, "Show Errors", NULL, ShowErrors, NULL);
|
||||
RegisterSetting(Set_HleAlistTask, Data_DWORD_General, "Hle Alist Task", NULL, HleAlistTask, NULL);
|
||||
RegisterSetting(Set_SyncCPU, Data_DWORD_General, "Sync CPU", NULL, SyncCPU, NULL);
|
||||
|
||||
// Compiler settings
|
||||
RegisterSetting(Set_CheckDest, Data_DWORD_General, "Check Destination Vector", NULL, Compiler.bDest, NULL);
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
#include <stdint.h>
|
||||
|
||||
extern uint16_t Set_AudioHle, Set_GraphicsHle, Set_AllocatedRdramSize, Set_DirectoryLog;
|
||||
extern bool GraphicsHle, AudioHle, ConditionalMove, HleAlistTask, RspMultiThreaded;
|
||||
extern bool GraphicsHle, AudioHle, ConditionalMove, SyncCPU, HleAlistTask, RspMultiThreaded;
|
||||
extern bool DebuggingEnabled, Profiling, IndvidualBlock, ShowErrors, BreakOnStart, LogRDP, LogX86Code;
|
||||
|
||||
enum class RSPCpuMethod
|
||||
{
|
||||
Interpreter = 0,
|
||||
Recompiler = 1,
|
||||
HighLevelEmulation = 2,
|
||||
RecompilerTasks = 2,
|
||||
HighLevelEmulation = 3,
|
||||
};
|
||||
|
||||
class CRSPSettings
|
||||
|
|
|
@ -10,6 +10,7 @@ enum
|
|||
Set_IndvidualBlock,
|
||||
Set_ShowErrors,
|
||||
Set_HleAlistTask,
|
||||
Set_SyncCPU,
|
||||
|
||||
// Compiler settings
|
||||
Set_CheckDest,
|
||||
|
|
|
@ -112,8 +112,9 @@ uint32_t DoRspCycles(uint32_t Cycles)
|
|||
RSPSystem.RunRecompiler();
|
||||
break;
|
||||
case RSPCpuMethod::Interpreter:
|
||||
case RSPCpuMethod::RecompilerTasks:
|
||||
case RSPCpuMethod::HighLevelEmulation:
|
||||
RSPSystem.RunInterpreterCPU(Cycles);
|
||||
RSPSystem.ExecuteOps((uint32_t)-1, (uint32_t)-1);
|
||||
break;
|
||||
}
|
||||
if (g_RSPDebugger != nullptr)
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
extern bool AudioHle, GraphicsHle;
|
||||
|
||||
uint32_t clz32(uint32_t val)
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include <Project64-rsp-core/Settings/RspSettings.h>
|
||||
#include <Project64-rsp-core/cpu/RSPOpcode.h>
|
||||
#include <Project64-rsp-core/cpu/RspPipelineStage.h>
|
||||
#include <Project64-rsp-core/cpu/RspTypes.h>
|
||||
|
@ -12,6 +13,7 @@ class RSPOp :
|
|||
{
|
||||
friend class CRSPSystem;
|
||||
friend class CRSPRecompilerOps;
|
||||
friend class CHleTask;
|
||||
|
||||
public:
|
||||
RSPOp(CRSPSystem & System);
|
||||
|
|
|
@ -37,6 +37,42 @@ const char * GPR_Strings[32] = {
|
|||
"RA",
|
||||
};
|
||||
|
||||
CGPRRegisters::CGPRRegisters(UWORD32 (&m_GPR)[32]) :
|
||||
GPR_R0(m_GPR[0].UW),
|
||||
GPR_AT(m_GPR[1].UW),
|
||||
GPR_V0(m_GPR[2].UW),
|
||||
GPR_V1(m_GPR[3].UW),
|
||||
GPR_A0(m_GPR[4].UW),
|
||||
GPR_A1(m_GPR[5].UW),
|
||||
GPR_A2(m_GPR[6].UW),
|
||||
GPR_A3(m_GPR[7].UW),
|
||||
GPR_T0(m_GPR[8].UW),
|
||||
GPR_T1(m_GPR[9].UW),
|
||||
GPR_T2(m_GPR[10].UW),
|
||||
GPR_T3(m_GPR[11].UW),
|
||||
GPR_T4(m_GPR[12].UW),
|
||||
GPR_T5(m_GPR[13].UW),
|
||||
GPR_T6(m_GPR[14].UW),
|
||||
GPR_T7(m_GPR[15].UW),
|
||||
GPR_S0(m_GPR[16].UW),
|
||||
GPR_S1(m_GPR[17].UW),
|
||||
GPR_S2(m_GPR[18].UW),
|
||||
GPR_S3(m_GPR[19].UW),
|
||||
GPR_S4(m_GPR[20].UW),
|
||||
GPR_S5(m_GPR[21].UW),
|
||||
GPR_S6(m_GPR[22].UW),
|
||||
GPR_S7(m_GPR[23].UW),
|
||||
GPR_T8(m_GPR[24].UW),
|
||||
GPR_T9(m_GPR[25].UW),
|
||||
GPR_K0(m_GPR[26].UW),
|
||||
GPR_K1(m_GPR[27].UW),
|
||||
GPR_GP(m_GPR[28].UW),
|
||||
GPR_SP(m_GPR[29].UW),
|
||||
GPR_S8(m_GPR[30].UW),
|
||||
GPR_RA(m_GPR[31].UW)
|
||||
{
|
||||
}
|
||||
|
||||
CRSPRegisters::CRSPRegisters() :
|
||||
VCOL(m_Flags[0].UB[0]),
|
||||
VCOH(m_Flags[0].UB[1]),
|
||||
|
|
|
@ -20,7 +20,8 @@ RSPRegisterHandler::RSPRegisterHandler(uint32_t * SignalProcessorInterface, uint
|
|||
m_IMEM(IMEM),
|
||||
m_DMEM(DMEM),
|
||||
m_PendingSPMemAddr(0),
|
||||
m_PendingSPDramAddr(0)
|
||||
m_PendingSPDramAddr(0),
|
||||
m_IgnoreWrites(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -39,7 +40,8 @@ RSPRegisterHandler::RSPRegisterHandler(CRSPSystem & System) :
|
|||
m_IMEM(System.m_IMEM),
|
||||
m_DMEM(System.m_DMEM),
|
||||
m_PendingSPMemAddr(0),
|
||||
m_PendingSPDramAddr(0)
|
||||
m_PendingSPDramAddr(0),
|
||||
m_IgnoreWrites(System.m_BaseSystem != nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -166,7 +168,7 @@ void RSPRegisterHandler::SP_DMA_WRITE()
|
|||
{
|
||||
CopyAmount = CopyLength;
|
||||
}
|
||||
if (CopyAmount > 0)
|
||||
if (CopyAmount > 0 && !m_IgnoreWrites)
|
||||
{
|
||||
memcpy(&Dest[WritePos], &Source[Pos], CopyAmount);
|
||||
}
|
||||
|
@ -183,7 +185,7 @@ void RSPRegisterHandler::SP_DMA_WRITE()
|
|||
{
|
||||
CopyAmount = CopyLength;
|
||||
}
|
||||
if (CopyAmount > 0)
|
||||
if (CopyAmount > 0 && !m_IgnoreWrites)
|
||||
{
|
||||
memcpy(&Dest[WritePos], &Source[Pos], CopyAmount);
|
||||
}
|
||||
|
|
|
@ -63,4 +63,5 @@ protected:
|
|||
uint8_t * m_DMEM;
|
||||
uint32_t m_PendingSPMemAddr;
|
||||
uint32_t m_PendingSPDramAddr;
|
||||
bool m_IgnoreWrites;
|
||||
};
|
||||
|
|
|
@ -71,6 +71,49 @@ enum
|
|||
MI_INTR_SP = 0x01, // Bit 0: SP INTR
|
||||
};
|
||||
|
||||
class CGPRRegisters
|
||||
{
|
||||
public:
|
||||
CGPRRegisters(UWORD32 (&m_GPR)[32]);
|
||||
|
||||
private:
|
||||
CGPRRegisters();
|
||||
|
||||
protected:
|
||||
uint32_t & GPR_R0;
|
||||
uint32_t & GPR_AT;
|
||||
uint32_t & GPR_V0;
|
||||
uint32_t & GPR_V1;
|
||||
uint32_t & GPR_A0;
|
||||
uint32_t & GPR_A1;
|
||||
uint32_t & GPR_A2;
|
||||
uint32_t & GPR_A3;
|
||||
uint32_t & GPR_T0;
|
||||
uint32_t & GPR_T1;
|
||||
uint32_t & GPR_T2;
|
||||
uint32_t & GPR_T3;
|
||||
uint32_t & GPR_T4;
|
||||
uint32_t & GPR_T5;
|
||||
uint32_t & GPR_T6;
|
||||
uint32_t & GPR_T7;
|
||||
uint32_t & GPR_S0;
|
||||
uint32_t & GPR_S1;
|
||||
uint32_t & GPR_S2;
|
||||
uint32_t & GPR_S3;
|
||||
uint32_t & GPR_S4;
|
||||
uint32_t & GPR_S5;
|
||||
uint32_t & GPR_S6;
|
||||
uint32_t & GPR_S7;
|
||||
uint32_t & GPR_T8;
|
||||
uint32_t & GPR_T9;
|
||||
uint32_t & GPR_K0;
|
||||
uint32_t & GPR_K1;
|
||||
uint32_t & GPR_GP;
|
||||
uint32_t & GPR_SP;
|
||||
uint32_t & GPR_S8;
|
||||
uint32_t & GPR_RA;
|
||||
};
|
||||
|
||||
class CRSPRegisters
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -2,15 +2,20 @@
|
|||
|
||||
enum RSPPIPELINE_STAGE
|
||||
{
|
||||
RSPPIPELINE_NORMAL = 0,
|
||||
RSPPIPELINE_DO_DELAY_SLOT = 1,
|
||||
RSPPIPELINE_DELAY_SLOT = 2,
|
||||
RSPPIPELINE_DELAY_SLOT_DONE = 3,
|
||||
RSPPIPELINE_DELAY_SLOT_EXIT = 4,
|
||||
RSPPIPELINE_DELAY_SLOT_EXIT_DONE = 5,
|
||||
RSPPIPELINE_JUMP = 6,
|
||||
RSPPIPELINE_SINGLE_STEP = 7,
|
||||
RSPPIPELINE_SINGLE_STEP_DONE = 8,
|
||||
RSPPIPELINE_FINISH_BLOCK = 9,
|
||||
RSPPIPELINE_FINISH_SUB_BLOCK = 10,
|
||||
RSPPIPELINE_NORMAL,
|
||||
RSPPIPELINE_DO_DELAY_SLOT,
|
||||
RSPPIPELINE_DO_DELAY_SLOT_EXIT,
|
||||
RSPPIPELINE_DO_DELAY_SLOT_TASK_EXIT,
|
||||
RSPPIPELINE_DELAY_SLOT,
|
||||
RSPPIPELINE_DELAY_SLOT_DONE,
|
||||
RSPPIPELINE_DELAY_SLOT_EXIT,
|
||||
RSPPIPELINE_DELAY_SLOT_EXIT_DONE,
|
||||
RSPPIPELINE_DELAY_SLOT_TASK_EXIT,
|
||||
RSPPIPELINE_DELAY_SLOT_TASK_EXIT_DONE,
|
||||
RSPPIPELINE_JUMP,
|
||||
RSPPIPELINE_SINGLE_STEP,
|
||||
RSPPIPELINE_SINGLE_STEP_DONE,
|
||||
RSPPIPELINE_FINISH_BLOCK,
|
||||
RSPPIPELINE_FINISH_SUB_BLOCK,
|
||||
RSPPIPELINE_FINISH_TASK_SUB_BLOCK,
|
||||
};
|
|
@ -11,6 +11,8 @@ CRSPSystem RSPSystem;
|
|||
|
||||
CRSPSystem::CRSPSystem() :
|
||||
CHleTask(*this),
|
||||
m_SyncSystem(nullptr),
|
||||
m_BaseSystem(nullptr),
|
||||
m_Recompiler(*this),
|
||||
m_RSPRegisterHandler(nullptr),
|
||||
m_Op(*this),
|
||||
|
@ -38,9 +40,10 @@ CRSPSystem::CRSPSystem() :
|
|||
m_DPC_BUFBUSY_REG(nullptr),
|
||||
m_DPC_PIPEBUSY_REG(nullptr),
|
||||
m_DPC_TMEM_REG(nullptr),
|
||||
CheckInterrupts(nullptr),
|
||||
ProcessDList(nullptr),
|
||||
ProcessRdpList(nullptr),
|
||||
CheckInterrupts(NullCheckInterrupts),
|
||||
ProcessDList(NullProcessDList),
|
||||
ProcessRdpList(NullProcessRdpList),
|
||||
m_SyncReg(nullptr),
|
||||
m_RdramSize(0)
|
||||
{
|
||||
m_OpCode.Value = 0;
|
||||
|
@ -48,6 +51,29 @@ CRSPSystem::CRSPSystem() :
|
|||
|
||||
CRSPSystem::~CRSPSystem()
|
||||
{
|
||||
if (m_SyncSystem->m_BaseSystem != nullptr)
|
||||
{
|
||||
if (m_IMEM != nullptr)
|
||||
{
|
||||
delete[] m_IMEM;
|
||||
m_IMEM = nullptr;
|
||||
}
|
||||
if (m_DMEM != nullptr)
|
||||
{
|
||||
delete[] m_DMEM;
|
||||
m_DMEM = nullptr;
|
||||
}
|
||||
}
|
||||
if (m_SyncReg != nullptr)
|
||||
{
|
||||
delete m_SyncReg;
|
||||
m_SyncReg = nullptr;
|
||||
}
|
||||
if (m_SyncSystem)
|
||||
{
|
||||
delete m_SyncSystem;
|
||||
m_SyncSystem = nullptr;
|
||||
}
|
||||
if (m_RSPRegisterHandler != nullptr)
|
||||
{
|
||||
delete m_RSPRegisterHandler;
|
||||
|
@ -61,29 +87,33 @@ void CRSPSystem::Reset(RSP_INFO & Info)
|
|||
|
||||
m_HEADER = Info.HEADER;
|
||||
m_RDRAM = Info.RDRAM;
|
||||
m_DMEM = Info.DMEM;
|
||||
m_IMEM = Info.IMEM;
|
||||
m_MI_INTR_REG = Info.MI_INTR_REG;
|
||||
m_SP_MEM_ADDR_REG = Info.SP_MEM_ADDR_REG;
|
||||
m_SP_DRAM_ADDR_REG = Info.SP_DRAM_ADDR_REG;
|
||||
m_SP_RD_LEN_REG = Info.SP_RD_LEN_REG;
|
||||
m_SP_WR_LEN_REG = Info.SP_WR_LEN_REG;
|
||||
m_SP_STATUS_REG = Info.SP_STATUS_REG;
|
||||
m_SP_DMA_FULL_REG = Info.SP_DMA_FULL_REG;
|
||||
m_SP_DMA_BUSY_REG = Info.SP_DMA_BUSY_REG;
|
||||
m_SP_PC_REG = Info.SP_PC_REG;
|
||||
m_SP_SEMAPHORE_REG = Info.SP_SEMAPHORE_REG;
|
||||
m_DPC_START_REG = Info.DPC_START_REG;
|
||||
m_DPC_END_REG = Info.DPC_END_REG;
|
||||
m_DPC_CURRENT_REG = Info.DPC_CURRENT_REG;
|
||||
m_DPC_STATUS_REG = Info.DPC_STATUS_REG;
|
||||
m_DPC_CLOCK_REG = Info.DPC_CLOCK_REG;
|
||||
m_DPC_BUFBUSY_REG = Info.DPC_BUFBUSY_REG;
|
||||
m_DPC_PIPEBUSY_REG = Info.DPC_PIPEBUSY_REG;
|
||||
m_DPC_TMEM_REG = Info.DPC_TMEM_REG;
|
||||
CheckInterrupts = Info.CheckInterrupts;
|
||||
ProcessDList = Info.ProcessDList;
|
||||
ProcessRdpList = Info.ProcessRdpList;
|
||||
|
||||
if (m_BaseSystem == nullptr)
|
||||
{
|
||||
m_DMEM = Info.DMEM;
|
||||
m_IMEM = Info.IMEM;
|
||||
m_MI_INTR_REG = Info.MI_INTR_REG;
|
||||
m_SP_MEM_ADDR_REG = Info.SP_MEM_ADDR_REG;
|
||||
m_SP_DRAM_ADDR_REG = Info.SP_DRAM_ADDR_REG;
|
||||
m_SP_RD_LEN_REG = Info.SP_RD_LEN_REG;
|
||||
m_SP_WR_LEN_REG = Info.SP_WR_LEN_REG;
|
||||
m_SP_STATUS_REG = Info.SP_STATUS_REG;
|
||||
m_SP_DMA_FULL_REG = Info.SP_DMA_FULL_REG;
|
||||
m_SP_DMA_BUSY_REG = Info.SP_DMA_BUSY_REG;
|
||||
m_SP_PC_REG = Info.SP_PC_REG;
|
||||
m_SP_SEMAPHORE_REG = Info.SP_SEMAPHORE_REG;
|
||||
m_DPC_START_REG = Info.DPC_START_REG;
|
||||
m_DPC_END_REG = Info.DPC_END_REG;
|
||||
m_DPC_CURRENT_REG = Info.DPC_CURRENT_REG;
|
||||
m_DPC_STATUS_REG = Info.DPC_STATUS_REG;
|
||||
m_DPC_CLOCK_REG = Info.DPC_CLOCK_REG;
|
||||
m_DPC_BUFBUSY_REG = Info.DPC_BUFBUSY_REG;
|
||||
m_DPC_PIPEBUSY_REG = Info.DPC_PIPEBUSY_REG;
|
||||
m_DPC_TMEM_REG = Info.DPC_TMEM_REG;
|
||||
CheckInterrupts = Info.CheckInterrupts;
|
||||
ProcessDList = Info.ProcessDList;
|
||||
ProcessRdpList = Info.ProcessRdpList;
|
||||
}
|
||||
|
||||
m_RdramSize = Set_AllocatedRdramSize != 0 ? GetSystemSetting(Set_AllocatedRdramSize) : 0;
|
||||
if (m_RdramSize == 0)
|
||||
|
@ -91,6 +121,11 @@ void CRSPSystem::Reset(RSP_INFO & Info)
|
|||
m_RdramSize = 0x00400000;
|
||||
}
|
||||
m_RSPRegisterHandler = new RSPRegisterHandlerPlugin(*this);
|
||||
|
||||
if (m_SyncSystem != nullptr)
|
||||
{
|
||||
m_SyncSystem->Reset(Info);
|
||||
}
|
||||
}
|
||||
|
||||
void CRSPSystem::RomClosed(void)
|
||||
|
@ -107,26 +142,32 @@ void CRSPSystem::RunRecompiler(void)
|
|||
m_Recompiler.RunCPU();
|
||||
}
|
||||
|
||||
uint32_t CRSPSystem::RunInterpreterCPU(uint32_t Cycles)
|
||||
void CRSPSystem::ExecuteOps(uint32_t Cycles, uint32_t TargetPC)
|
||||
{
|
||||
uint32_t CycleCount;
|
||||
RSP_Running = true;
|
||||
if (g_RSPDebugger != nullptr)
|
||||
{
|
||||
g_RSPDebugger->StartingCPU();
|
||||
}
|
||||
CycleCount = 0;
|
||||
uint32_t & GprR0 = m_Reg.m_GPR[0].UW;
|
||||
uint32_t & ProgramCounter = *m_SP_PC_REG;
|
||||
while (RSP_Running)
|
||||
while (RSP_Running && Cycles > 0)
|
||||
{
|
||||
if (g_RSPDebugger != nullptr)
|
||||
{
|
||||
g_RSPDebugger->BeforeExecuteOp();
|
||||
}
|
||||
if (TargetPC != -1 && (ProgramCounter & 0xFFC) == TargetPC)
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_OpCode.Value = *(uint32_t *)(m_IMEM + (ProgramCounter & 0xFFC));
|
||||
(m_Op.*(m_Op.Jump_Opcode[m_OpCode.op]))();
|
||||
GprR0 = 0x00000000; // MIPS $zero hard-wired to 0
|
||||
if (Cycles != (uint32_t)-1)
|
||||
{
|
||||
Cycles -= 1;
|
||||
}
|
||||
|
||||
switch (m_NextInstruction)
|
||||
{
|
||||
|
@ -152,5 +193,167 @@ uint32_t CRSPSystem::RunInterpreterCPU(uint32_t Cycles)
|
|||
break;
|
||||
}
|
||||
}
|
||||
return Cycles;
|
||||
}
|
||||
|
||||
void CRSPSystem::SetupSyncCPU()
|
||||
{
|
||||
if (m_SyncSystem == nullptr)
|
||||
{
|
||||
m_SyncSystem = new CRSPSystem();
|
||||
m_SyncSystem->m_BaseSystem = this;
|
||||
m_SyncSystem->m_HEADER = m_HEADER;
|
||||
m_SyncSystem->m_RDRAM = m_RDRAM;
|
||||
m_SyncSystem->m_IMEM = new uint8_t[0x1000];
|
||||
m_SyncSystem->m_DMEM = new uint8_t[0x1000];
|
||||
m_SyncSystem->m_SyncReg = new uint32_t[18];
|
||||
m_SyncSystem->m_MI_INTR_REG = &m_SyncSystem->m_SyncReg[0];
|
||||
m_SyncSystem->m_SP_MEM_ADDR_REG = &m_SyncSystem->m_SyncReg[1];
|
||||
m_SyncSystem->m_SP_DRAM_ADDR_REG = &m_SyncSystem->m_SyncReg[2];
|
||||
m_SyncSystem->m_SP_RD_LEN_REG = &m_SyncSystem->m_SyncReg[3];
|
||||
m_SyncSystem->m_SP_WR_LEN_REG = &m_SyncSystem->m_SyncReg[4];
|
||||
m_SyncSystem->m_SP_STATUS_REG = &m_SyncSystem->m_SyncReg[5];
|
||||
m_SyncSystem->m_SP_DMA_FULL_REG = &m_SyncSystem->m_SyncReg[6];
|
||||
m_SyncSystem->m_SP_DMA_BUSY_REG = &m_SyncSystem->m_SyncReg[7];
|
||||
m_SyncSystem->m_SP_PC_REG = &m_SyncSystem->m_SyncReg[8];
|
||||
m_SyncSystem->m_SP_SEMAPHORE_REG = &m_SyncSystem->m_SyncReg[9];
|
||||
m_SyncSystem->m_DPC_START_REG = &m_SyncSystem->m_SyncReg[10];
|
||||
m_SyncSystem->m_DPC_END_REG = &m_SyncSystem->m_SyncReg[11];
|
||||
m_SyncSystem->m_DPC_CURRENT_REG = &m_SyncSystem->m_SyncReg[12];
|
||||
m_SyncSystem->m_DPC_STATUS_REG = &m_SyncSystem->m_SyncReg[13];
|
||||
m_SyncSystem->m_DPC_CLOCK_REG = &m_SyncSystem->m_SyncReg[14];
|
||||
m_SyncSystem->m_DPC_BUFBUSY_REG = &m_SyncSystem->m_SyncReg[15];
|
||||
m_SyncSystem->m_DPC_PIPEBUSY_REG = &m_SyncSystem->m_SyncReg[16];
|
||||
m_SyncSystem->m_DPC_TMEM_REG = &m_SyncSystem->m_SyncReg[17];
|
||||
|
||||
m_SyncSystem->m_Reg.Reset();
|
||||
m_SyncSystem->m_RdramSize = m_RdramSize;
|
||||
m_SyncSystem->m_RSPRegisterHandler = new RSPRegisterHandlerPlugin(*m_SyncSystem);
|
||||
}
|
||||
if (m_IMEM != nullptr)
|
||||
{
|
||||
memcpy(m_SyncSystem->m_IMEM, m_IMEM, 0x1000);
|
||||
}
|
||||
if (m_DMEM != nullptr)
|
||||
{
|
||||
memcpy(m_SyncSystem->m_DMEM, m_DMEM, 0x1000);
|
||||
}
|
||||
*m_SyncSystem->m_MI_INTR_REG = *m_MI_INTR_REG;
|
||||
*m_SyncSystem->m_SP_MEM_ADDR_REG = *m_SP_MEM_ADDR_REG;
|
||||
*m_SyncSystem->m_SP_DRAM_ADDR_REG = *m_SP_DRAM_ADDR_REG;
|
||||
*m_SyncSystem->m_SP_RD_LEN_REG = *m_SP_RD_LEN_REG;
|
||||
*m_SyncSystem->m_SP_WR_LEN_REG = *m_SP_WR_LEN_REG;
|
||||
*m_SyncSystem->m_SP_STATUS_REG = *m_SP_STATUS_REG;
|
||||
*m_SyncSystem->m_SP_DMA_FULL_REG = *m_SP_DMA_FULL_REG;
|
||||
*m_SyncSystem->m_SP_DMA_BUSY_REG = *m_SP_DMA_BUSY_REG;
|
||||
*m_SyncSystem->m_SP_PC_REG = *m_SP_PC_REG;
|
||||
*m_SyncSystem->m_SP_SEMAPHORE_REG = *m_SP_SEMAPHORE_REG;
|
||||
*m_SyncSystem->m_DPC_START_REG = *m_DPC_START_REG;
|
||||
*m_SyncSystem->m_DPC_END_REG = *m_DPC_END_REG;
|
||||
*m_SyncSystem->m_DPC_CURRENT_REG = *m_DPC_CURRENT_REG;
|
||||
*m_SyncSystem->m_DPC_STATUS_REG = *m_DPC_STATUS_REG;
|
||||
*m_SyncSystem->m_DPC_CLOCK_REG = *m_DPC_CLOCK_REG;
|
||||
*m_SyncSystem->m_DPC_BUFBUSY_REG = *m_DPC_BUFBUSY_REG;
|
||||
*m_SyncSystem->m_DPC_PIPEBUSY_REG = *m_DPC_PIPEBUSY_REG;
|
||||
*m_SyncSystem->m_DPC_TMEM_REG = *m_DPC_TMEM_REG;
|
||||
}
|
||||
|
||||
bool CRSPSystem::IsSyncSystem(void)
|
||||
{
|
||||
return m_BaseSystem != nullptr;
|
||||
}
|
||||
|
||||
CRSPSystem * CRSPSystem::SyncSystem(void)
|
||||
{
|
||||
if (m_SyncSystem == nullptr)
|
||||
{
|
||||
SetupSyncCPU();
|
||||
}
|
||||
return m_SyncSystem;
|
||||
}
|
||||
|
||||
void CRSPSystem::BasicSyncCheck(void)
|
||||
{
|
||||
bool SyncFailed = false;
|
||||
if (memcmp(m_IMEM, m_SyncSystem->m_IMEM, 0x1000) != 0)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (memcmp(m_DMEM, m_SyncSystem->m_DMEM, 0x1000) != 0)
|
||||
{
|
||||
SyncFailed = true;
|
||||
for (uint32_t i = 0, n = 0x1000; i < n; i++)
|
||||
{
|
||||
if (m_DMEM[i] != m_SyncSystem->m_DMEM[i])
|
||||
{
|
||||
SyncFailed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*m_MI_INTR_REG != *m_SyncSystem->m_MI_INTR_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_MEM_ADDR_REG != *m_SyncSystem->m_SP_MEM_ADDR_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_DRAM_ADDR_REG != *m_SyncSystem->m_SP_DRAM_ADDR_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_RD_LEN_REG != *m_SyncSystem->m_SP_RD_LEN_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_WR_LEN_REG != *m_SyncSystem->m_SP_WR_LEN_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_STATUS_REG != *m_SyncSystem->m_SP_STATUS_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_DMA_FULL_REG != *m_SyncSystem->m_SP_DMA_FULL_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_DMA_BUSY_REG != *m_SyncSystem->m_SP_DMA_BUSY_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_PC_REG != *m_SyncSystem->m_SP_PC_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (*m_SP_SEMAPHORE_REG != *m_SyncSystem->m_SP_SEMAPHORE_REG)
|
||||
{
|
||||
SyncFailed = true;
|
||||
}
|
||||
if (SyncFailed)
|
||||
{
|
||||
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
void * CRSPSystem::operator new(size_t size)
|
||||
{
|
||||
return _aligned_malloc(size, 16);
|
||||
}
|
||||
|
||||
void CRSPSystem::operator delete(void * ptr)
|
||||
{
|
||||
_aligned_free(ptr);
|
||||
}
|
||||
|
||||
void CRSPSystem::NullProcessDList(void)
|
||||
{
|
||||
}
|
||||
|
||||
void CRSPSystem::NullProcessRdpList(void)
|
||||
{
|
||||
}
|
||||
|
||||
void CRSPSystem::NullCheckInterrupts(void)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -33,12 +33,24 @@ public:
|
|||
void RomClosed(void);
|
||||
|
||||
void RunRecompiler(void);
|
||||
uint32_t RunInterpreterCPU(uint32_t Cycles);
|
||||
void ExecuteOps(uint32_t Cycles, uint32_t TargetPC);
|
||||
void SetupSyncCPU();
|
||||
bool IsSyncSystem(void);
|
||||
CRSPSystem * SyncSystem(void);
|
||||
void BasicSyncCheck(void);
|
||||
void * operator new(size_t size);
|
||||
void operator delete(void * ptr);
|
||||
|
||||
private:
|
||||
CRSPSystem(const CRSPSystem &);
|
||||
CRSPSystem & operator=(const CRSPSystem &);
|
||||
|
||||
static void NullProcessDList(void);
|
||||
static void NullProcessRdpList(void);
|
||||
static void NullCheckInterrupts(void);
|
||||
|
||||
CRSPSystem * m_SyncSystem;
|
||||
CRSPSystem * m_BaseSystem;
|
||||
CRSPRecompiler m_Recompiler;
|
||||
RSPRegisterHandlerPlugin * m_RSPRegisterHandler;
|
||||
CRSPRegisters m_Reg;
|
||||
|
@ -69,6 +81,7 @@ private:
|
|||
uint32_t * m_DPC_PIPEBUSY_REG;
|
||||
uint32_t * m_DPC_TMEM_REG;
|
||||
uint32_t m_RdramSize;
|
||||
uint32_t * m_SyncReg;
|
||||
void (*CheckInterrupts)(void);
|
||||
void (*ProcessDList)(void);
|
||||
void (*ProcessRdpList)(void);
|
||||
|
|
|
@ -179,9 +179,11 @@ void FixMenuState(void)
|
|||
|
||||
CheckMenuItem(hRSPMenu, ID_CPUMETHOD_RECOMPILER, MF_BYCOMMAND | ((RSPCpuMethod)GetSetting(Set_CPUCore) == RSPCpuMethod::Recompiler ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_CPUMETHOD_INTERPT, MF_BYCOMMAND | ((RSPCpuMethod)GetSetting(Set_CPUCore) == RSPCpuMethod::Interpreter ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_CPUMETHOD_RECOMPILER_TASKS, MF_BYCOMMAND | ((RSPCpuMethod)GetSetting(Set_CPUCore) == RSPCpuMethod::RecompilerTasks ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_CPUMETHOD_HLE, MF_BYCOMMAND | ((RSPCpuMethod)GetSetting(Set_CPUCore) == RSPCpuMethod::HighLevelEmulation ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_BREAKONSTARTOFTASK, MF_BYCOMMAND | (BreakOnStart ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_LOGRDPCOMMANDS, MF_BYCOMMAND | (LogRDP ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_SETTINGS_SYNCCPU, MF_BYCOMMAND | (SyncCPU ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_SETTINGS_HLEALISTTASK, MF_BYCOMMAND | (HleAlistTask ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_SETTINGS_LOGX86CODE, MF_BYCOMMAND | (LogX86Code ? MFS_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(hRSPMenu, ID_SETTINGS_MULTITHREADED, MF_BYCOMMAND | (MultiThreadedDefault ? MFS_CHECKED : MF_UNCHECKED));
|
||||
|
@ -384,6 +386,13 @@ void ProcessMenuItem(int32_t ID)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case ID_SETTINGS_SYNCCPU:
|
||||
{
|
||||
bool Checked = (GetMenuState(hRSPMenu, ID_SETTINGS_SYNCCPU, MF_BYCOMMAND) & MFS_CHECKED) != 0;
|
||||
CheckMenuItem(hRSPMenu, ID_SETTINGS_SYNCCPU, MF_BYCOMMAND | (Checked ? MFS_UNCHECKED : MFS_CHECKED));
|
||||
SetSetting(Set_SyncCPU, !Checked);
|
||||
break;
|
||||
}
|
||||
case ID_SETTINGS_HLEALISTTASK:
|
||||
{
|
||||
bool Checked = (GetMenuState(hRSPMenu, ID_SETTINGS_HLEALISTTASK, MF_BYCOMMAND) & MFS_CHECKED) != 0;
|
||||
|
@ -410,6 +419,10 @@ void ProcessMenuItem(int32_t ID)
|
|||
SetSetting(Set_CPUCore, (int)RSPCpuMethod::Interpreter);
|
||||
FixMenuState();
|
||||
break;
|
||||
case ID_CPUMETHOD_RECOMPILER_TASKS:
|
||||
SetSetting(Set_CPUCore, (int)RSPCpuMethod::RecompilerTasks);
|
||||
FixMenuState();
|
||||
break;
|
||||
case ID_CPUMETHOD_HLE:
|
||||
SetSetting(Set_CPUCore, (int)RSPCpuMethod::HighLevelEmulation);
|
||||
FixMenuState();
|
||||
|
@ -551,7 +564,6 @@ BOOL CALLBACK ConfigDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM /*lParam
|
|||
hWndItem = GetDlgItem(hDlg, IDC_COMPILER_SELECT);
|
||||
ComboBox_AddString(hWndItem, "Interpreter");
|
||||
ComboBox_AddString(hWndItem, "Recompiler");
|
||||
//ComboBox_SetCurSel(hWndItem, g_CPUCore);
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
||||
|
@ -596,6 +608,7 @@ EXPORT void EnableDebugging(int Enabled)
|
|||
IndvidualBlock = GetSetting(Set_IndvidualBlock) != 0;
|
||||
ShowErrors = GetSetting(Set_ShowErrors) != 0;
|
||||
HleAlistTask = GetSetting(Set_HleAlistTask) != 0;
|
||||
SyncCPU = GetSetting(Set_SyncCPU) != 0;
|
||||
|
||||
Compiler.bDest = GetSetting(Set_CheckDest) != 0;
|
||||
Compiler.bAccum = GetSetting(Set_Accum) != 0;
|
||||
|
|
|
@ -170,6 +170,7 @@ BEGIN
|
|||
POPUP "CPU Method"
|
||||
BEGIN
|
||||
MENUITEM "Recompiler", ID_CPUMETHOD_RECOMPILER
|
||||
MENUITEM "Recompiler Tasks", ID_CPUMETHOD_RECOMPILER_TASKS
|
||||
MENUITEM "Interpreter", ID_CPUMETHOD_INTERPT
|
||||
MENUITEM "HLE", ID_CPUMETHOD_HLE
|
||||
END
|
||||
|
@ -196,6 +197,7 @@ BEGIN
|
|||
MENUITEM "Break on start of task", ID_BREAKONSTARTOFTASK
|
||||
MENUITEM "Log RDP Commands", ID_LOGRDPCOMMANDS
|
||||
MENUITEM "Log X86 code", ID_SETTINGS_LOGX86CODE
|
||||
MENUITEM "Sync CPU", ID_SETTINGS_SYNCCPU
|
||||
MENUITEM "Multithreaded", ID_SETTINGS_MULTITHREADED
|
||||
END
|
||||
END
|
||||
|
|
|
@ -40,13 +40,15 @@
|
|||
#define ID_SETTINGS_MULTITHREADED 5020
|
||||
#define ID_SETTINGS_HLEALISTTASK 5021
|
||||
#define ID_CPUMETHOD_HLE 5022
|
||||
#define ID_CPUMETHOD_RECOMPILER_TASKS 5023
|
||||
#define ID_SETTINGS_SYNCCPU 5024
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 108
|
||||
#define _APS_NEXT_COMMAND_VALUE 5023
|
||||
#define _APS_NEXT_COMMAND_VALUE 5025
|
||||
#define _APS_NEXT_CONTROL_VALUE 1032
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue