Compare commits

...

4 Commits

Author SHA1 Message Date
zilmar 7cb0c258a1 GLideN64: Slight clean up of project file 2024-09-19 12:16:46 +09:30
zilmar c098a6a464 RSP: Be able to compile sections based off tasks 2024-09-19 12:15:11 +09:30
zilmar 3340c032c3 RSP: Move CompilePC into RspRecompilerCPU 2024-09-19 08:31:28 +09:30
zilmar df9b04bb5b RSP: Change RunInterpreterCPU to ExecuteOps 2024-09-12 15:13:45 +09:30
26 changed files with 1136 additions and 421 deletions

View File

@ -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" />

View File

@ -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>

View File

@ -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;

View File

@ -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;
};

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -10,6 +10,7 @@ enum
Set_IndvidualBlock,
Set_ShowErrors,
Set_HleAlistTask,
Set_SyncCPU,
// Compiler settings
Set_CheckDest,

View File

@ -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)

View File

@ -12,8 +12,6 @@
#include <float.h>
#include <math.h>
extern bool AudioHle, GraphicsHle;
uint32_t clz32(uint32_t val)
{
#if defined(__GNUC__)

View File

@ -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);

View File

@ -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]),

View File

@ -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);
}

View File

@ -63,4 +63,5 @@ protected:
uint8_t * m_DMEM;
uint32_t m_PendingSPMemAddr;
uint32_t m_PendingSPDramAddr;
bool m_IgnoreWrites;
};

View File

@ -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:

View File

@ -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,
};

View File

@ -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,6 +87,9 @@ void CRSPSystem::Reset(RSP_INFO & Info)
m_HEADER = Info.HEADER;
m_RDRAM = Info.RDRAM;
if (m_BaseSystem == nullptr)
{
m_DMEM = Info.DMEM;
m_IMEM = Info.IMEM;
m_MI_INTR_REG = Info.MI_INTR_REG;
@ -84,6 +113,7 @@ void CRSPSystem::Reset(RSP_INFO & Info)
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)
{
}

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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