mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-09-13 22:07:34 -05:00
Core and DSPCore now have mutual dependencies, which breaks the
assumption on the part of many linkers that static libraries make up a DAG, so merge libdspcore into libcore. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6967 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
@@ -27,6 +27,35 @@ set(SRCS Src/ActionReplay.cpp
|
||||
Src/Debugger/Debugger_SymbolMap.cpp
|
||||
Src/Debugger/Dump.cpp
|
||||
Src/Debugger/PPCDebugInterface.cpp
|
||||
Src/DSP/assemble.cpp
|
||||
Src/DSP/disassemble.cpp
|
||||
Src/DSP/DSPAccelerator.cpp
|
||||
Src/DSP/DSPIntCCUtil.cpp
|
||||
Src/DSP/DSPIntExtOps.cpp
|
||||
Src/DSP/DSPHWInterface.cpp
|
||||
Src/DSP/DSPMemoryMap.cpp
|
||||
Src/DSP/DSPStacks.cpp
|
||||
Src/DSP/DSPAnalyzer.cpp
|
||||
Src/DSP/DspIntArithmetic.cpp
|
||||
Src/DSP/DspIntBranch.cpp
|
||||
Src/DSP/DspIntLoadStore.cpp
|
||||
Src/DSP/DspIntMisc.cpp
|
||||
Src/DSP/DspIntMultiplier.cpp
|
||||
Src/DSP/DSPEmitter.cpp
|
||||
Src/DSP/DSPCodeUtil.cpp
|
||||
Src/DSP/LabelMap.cpp
|
||||
Src/DSP/DSPInterpreter.cpp
|
||||
Src/DSP/DSPCore.cpp
|
||||
Src/DSP/DSPTables.cpp
|
||||
Src/DSP/Jit/DSPJitRegCache.cpp
|
||||
Src/DSP/Jit/DSPJitExtOps.cpp
|
||||
Src/DSP/Jit/DSPJitBranch.cpp
|
||||
Src/DSP/Jit/DSPJitCCUtil.cpp
|
||||
Src/DSP/Jit/DSPJitArithmetic.cpp
|
||||
Src/DSP/Jit/DSPJitLoadStore.cpp
|
||||
Src/DSP/Jit/DSPJitMultiplier.cpp
|
||||
Src/DSP/Jit/DSPJitUtil.cpp
|
||||
Src/DSP/Jit/DSPJitMisc.cpp
|
||||
Src/HLE/HLE.cpp
|
||||
Src/HLE/HLE_Misc.cpp
|
||||
Src/HLE/HLE_OS.cpp
|
||||
@@ -154,7 +183,7 @@ set(SRCS Src/ActionReplay.cpp
|
||||
Src/PowerPC/JitCommon/JitCache.cpp
|
||||
Src/PowerPC/JitCommon/Jit_Util.cpp)
|
||||
|
||||
set(LIBS bdisasm inputcommon dspcore lua sfml-network)
|
||||
set(LIBS bdisasm inputcommon core lua sfml-network)
|
||||
|
||||
if(WIN32)
|
||||
set(SRCS ${SRCS} Src/HW/BBA-TAP/TAP_Win32.cpp Src/stdafx.cpp
|
||||
@@ -181,4 +210,6 @@ endif(OPROFILE_FOUND)
|
||||
|
||||
add_library(core STATIC ${SRCS})
|
||||
target_link_libraries(core ${LIBS})
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
add_definitions(-fPIC)
|
||||
endif()
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\DSPCore\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
@@ -116,7 +116,7 @@
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\DSPCore\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
@@ -191,7 +191,7 @@
|
||||
FavorSizeOrSpeed="1"
|
||||
OmitFramePointers="true"
|
||||
EnableFiberSafeOptimizations="false"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\DSPCore\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0"
|
||||
StringPooling="true"
|
||||
RuntimeLibrary="0"
|
||||
@@ -269,7 +269,7 @@
|
||||
OmitFramePointers="true"
|
||||
EnableFiberSafeOptimizations="false"
|
||||
WholeProgramOptimization="false"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\DSPCore\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0"
|
||||
StringPooling="true"
|
||||
RuntimeLibrary="0"
|
||||
@@ -344,7 +344,7 @@
|
||||
FavorSizeOrSpeed="1"
|
||||
OmitFramePointers="true"
|
||||
EnableFiberSafeOptimizations="false"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\DSPCore\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
PreprocessorDefinitions="NDEBUG;_LIB;DEBUGFAST;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0"
|
||||
RuntimeLibrary="0"
|
||||
BufferSecurityCheck="true"
|
||||
@@ -419,7 +419,7 @@
|
||||
FavorSizeOrSpeed="1"
|
||||
OmitFramePointers="true"
|
||||
EnableFiberSafeOptimizations="false"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\DSPCore\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
AdditionalIncludeDirectories=".\Core\Core\Src\Debugger;..\Common\Src;..\DiscIO\Src;..\AudioCommon\Src;..\..\Core\InputCommon\Src;..\..\PluginSpecs;..\..\..\Externals\LZO;..\..\..\Externals\Bochs_disasm;..\..\..\Externals\zlib;..\..\..\Externals\Lua;..\..\..\Externals\SFML\include"
|
||||
PreprocessorDefinitions="NDEBUG;_LIB;DEBUGFAST;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0"
|
||||
RuntimeLibrary="0"
|
||||
BufferSecurityCheck="false"
|
||||
@@ -465,6 +465,218 @@
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Interpreter"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DspIntArithmetic.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DspIntBranch.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPIntCCUtil.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPIntCCUtil.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPIntExtOps.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPIntExtOps.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DspIntLoadStore.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DspIntMisc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DspIntMultiplier.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPIntUtil.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="JIT"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPEmitter.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPEmitter.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitArithmetic.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitLoadStore.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitCCUtil.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitExtOps.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitBranch.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitMisc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitMultiplier.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitRegCache.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitRegCache.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitUtil.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\Jit\DSPJitUtil.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\assemble.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\assemble.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\disassemble.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\disassemble.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPAccelerator.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPAccelerator.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPAnalyzer.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPAnalyzer.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPBreakpoints.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPCodeUtil.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPCodeUtil.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPCommon.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPCore.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPCore.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPHost.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPHWInterface.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPHWInterface.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPInterpreter.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPInterpreter.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPMemoryMap.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPMemoryMap.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPStacks.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPStacks.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPTables.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\DSPTables.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\LabelMap.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DSP\LabelMap.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\SConscript"
|
||||
>
|
||||
</File>
|
||||
<Filter
|
||||
Name="HLE"
|
||||
>
|
||||
|
||||
@@ -275,6 +275,35 @@
|
||||
<ClCompile Include="Src\Debugger\Debugger_SymbolMap.cpp" />
|
||||
<ClCompile Include="Src\Debugger\Dump.cpp" />
|
||||
<ClCompile Include="Src\Debugger\PPCDebugInterface.cpp" />
|
||||
<ClCompile Include="Src\DSP\assemble.cpp" />
|
||||
<ClCompile Include="Src\DSP\disassemble.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPAccelerator.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPAnalyzer.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPCodeUtil.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPCore.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPEmitter.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPHWInterface.cpp" />
|
||||
<ClCompile Include="Src\DSP\DspIntArithmetic.cpp" />
|
||||
<ClCompile Include="Src\DSP\DspIntBranch.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPIntCCUtil.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPInterpreter.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPIntExtOps.cpp" />
|
||||
<ClCompile Include="Src\DSP\DspIntLoadStore.cpp" />
|
||||
<ClCompile Include="Src\DSP\DspIntMisc.cpp" />
|
||||
<ClCompile Include="Src\DSP\DspIntMultiplier.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPMemoryMap.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPStacks.cpp" />
|
||||
<ClCompile Include="Src\DSP\DSPTables.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitArithmetic.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitBranch.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitCCUtil.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitExtOps.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitLoadStore.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitMisc.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitMultiplier.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitRegCache.cpp" />
|
||||
<ClCompile Include="Src\DSP\Jit\DSPJitUtil.cpp" />
|
||||
<ClCompile Include="Src\DSP\LabelMap.cpp" />
|
||||
<ClCompile Include="Src\GeckoCode.cpp" />
|
||||
<ClCompile Include="Src\GeckoCodeConfig.cpp" />
|
||||
<ClCompile Include="Src\HLE\HLE.cpp" />
|
||||
@@ -421,6 +450,27 @@
|
||||
<ClInclude Include="Src\Debugger\GCELF.h" />
|
||||
<ClInclude Include="Src\Debugger\PPCDebugInterface.h" />
|
||||
<ClInclude Include="Src\DolLoader.h" />
|
||||
<ClInclude Include="Src\DSP\assemble.h" />
|
||||
<ClInclude Include="Src\DSP\disassemble.h" />
|
||||
<ClInclude Include="Src\DSP\DSPAccelerator.h" />
|
||||
<ClInclude Include="Src\DSP\DSPAnalyzer.h" />
|
||||
<ClInclude Include="Src\DSP\DSPBreakpoints.h" />
|
||||
<ClInclude Include="Src\DSP\DSPCodeUtil.h" />
|
||||
<ClInclude Include="Src\DSP\DSPCommon.h" />
|
||||
<ClInclude Include="Src\DSP\DSPCore.h" />
|
||||
<ClInclude Include="Src\DSP\DSPEmitter.h" />
|
||||
<ClInclude Include="Src\DSP\DSPHost.h" />
|
||||
<ClInclude Include="Src\DSP\DSPHWInterface.h" />
|
||||
<ClInclude Include="Src\DSP\DSPIntCCUtil.h" />
|
||||
<ClInclude Include="Src\DSP\DSPInterpreter.h" />
|
||||
<ClInclude Include="Src\DSP\DSPIntExtOps.h" />
|
||||
<ClInclude Include="Src\DSP\DSPIntUtil.h" />
|
||||
<ClInclude Include="Src\DSP\DSPMemoryMap.h" />
|
||||
<ClInclude Include="Src\DSP\DSPStacks.h" />
|
||||
<ClInclude Include="Src\DSP\DSPTables.h" />
|
||||
<ClInclude Include="Src\DSP\Jit\DSPJitRegCache.h" />
|
||||
<ClInclude Include="Src\DSP\Jit\DSPJitUtil.h" />
|
||||
<ClInclude Include="Src\DSP\LabelMap.h" />
|
||||
<ClInclude Include="Src\GeckoCode.h" />
|
||||
<ClInclude Include="Src\GeckoCodeConfig.h" />
|
||||
<ClInclude Include="Src\HLE\HLE.h" />
|
||||
@@ -542,4 +592,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
176
Source/Core/Core/Src/DSP/DSPAccelerator.cpp
Normal file
176
Source/Core/Core/Src/DSP/DSPAccelerator.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "DSPCore.h"
|
||||
#include "DSPHost.h"
|
||||
#include "DSPHWInterface.h"
|
||||
#include "DSPInterpreter.h"
|
||||
|
||||
// The hardware adpcm decoder :)
|
||||
static s16 ADPCM_Step(u32& _rSamplePos)
|
||||
{
|
||||
const s16 *pCoefTable = (const s16 *)&g_dsp.ifx_regs[DSP_COEF_A1_0];
|
||||
|
||||
if (((_rSamplePos) & 15) == 0)
|
||||
{
|
||||
g_dsp.ifx_regs[DSP_PRED_SCALE] = DSPHost_ReadHostMemory((_rSamplePos & ~15) >> 1);
|
||||
_rSamplePos += 2;
|
||||
}
|
||||
|
||||
int scale = 1 << (g_dsp.ifx_regs[DSP_PRED_SCALE] & 0xF);
|
||||
int coef_idx = (g_dsp.ifx_regs[DSP_PRED_SCALE] >> 4) & 0x7;
|
||||
|
||||
s32 coef1 = pCoefTable[coef_idx * 2 + 0];
|
||||
s32 coef2 = pCoefTable[coef_idx * 2 + 1];
|
||||
|
||||
int temp = (_rSamplePos & 1) ?
|
||||
(DSPHost_ReadHostMemory(_rSamplePos >> 1) & 0xF) :
|
||||
(DSPHost_ReadHostMemory(_rSamplePos >> 1) >> 4);
|
||||
|
||||
if (temp >= 8)
|
||||
temp -= 16;
|
||||
|
||||
// 0x400 = 0.5 in 11-bit fixed point
|
||||
int val = (scale * temp) + ((0x400 + coef1 * (s16)g_dsp.ifx_regs[DSP_YN1] + coef2 * (s16)g_dsp.ifx_regs[DSP_YN2]) >> 11);
|
||||
|
||||
// Clamp values.
|
||||
if (val > 0x7FFF)
|
||||
val = 0x7FFF;
|
||||
else if (val < -0x7FFF)
|
||||
val = -0x7FFF;
|
||||
|
||||
g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
|
||||
g_dsp.ifx_regs[DSP_YN1] = val;
|
||||
|
||||
_rSamplePos++;
|
||||
|
||||
// The advanced interpolation (linear, polyphase,...) is done by the UCode,
|
||||
// so we don't need to bother with it here.
|
||||
return val;
|
||||
}
|
||||
|
||||
u16 dsp_read_aram_d3()
|
||||
{
|
||||
// Zelda ucode reads ARAM through 0xffd3.
|
||||
const u32 EndAddress = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL];
|
||||
u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];
|
||||
u16 val = 0;
|
||||
|
||||
switch (g_dsp.ifx_regs[DSP_FORMAT]) {
|
||||
case 0x5: // u8 reads
|
||||
val = DSPHost_ReadHostMemory(Address);
|
||||
Address++;
|
||||
break;
|
||||
case 0x6: // u16 reads
|
||||
val = (DSPHost_ReadHostMemory(Address*2) << 8) | DSPHost_ReadHostMemory(Address*2 + 1);
|
||||
Address++;
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(DSPLLE, "dsp_read_aram_d3() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Address >= EndAddress)
|
||||
{
|
||||
// Set address back to start address. (never seen this here!)
|
||||
Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];
|
||||
}
|
||||
|
||||
g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
|
||||
g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
|
||||
return val;
|
||||
}
|
||||
|
||||
void dsp_write_aram_d3(u16 value)
|
||||
{
|
||||
// Zelda ucode writes a bunch of zeros to ARAM through d3 during
|
||||
// initialization. Don't know if it ever does it later, too.
|
||||
u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];
|
||||
|
||||
switch (g_dsp.ifx_regs[DSP_FORMAT]) {
|
||||
case 0xA: // u16 writes
|
||||
DSPHost_WriteHostMemory(value >> 8, Address*2);
|
||||
DSPHost_WriteHostMemory(value & 0xFF, Address*2 + 1);
|
||||
Address++;
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(DSPLLE, "dsp_write_aram_d3() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]);
|
||||
break;
|
||||
}
|
||||
|
||||
g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
|
||||
g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
|
||||
}
|
||||
|
||||
u16 dsp_read_accelerator()
|
||||
{
|
||||
const u32 EndAddress = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL];
|
||||
u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];
|
||||
u16 val;
|
||||
|
||||
// let's do the "hardware" decode DSP_FORMAT is interesting - the Zelda
|
||||
// ucode seems to indicate that the bottom two bits specify the "read size"
|
||||
// and the address multiplier. The bits above that may be things like sign
|
||||
// extention and do/do not use ADPCM. It also remains to be figured out
|
||||
// whether there's a difference between the usual accelerator "read
|
||||
// address" and 0xd3.
|
||||
switch (g_dsp.ifx_regs[DSP_FORMAT])
|
||||
{
|
||||
case 0x00: // ADPCM audio
|
||||
val = ADPCM_Step(Address);
|
||||
break;
|
||||
case 0x0A: // 16-bit PCM audio
|
||||
val = (DSPHost_ReadHostMemory(Address*2) << 8) | DSPHost_ReadHostMemory(Address*2 + 1);
|
||||
g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
|
||||
g_dsp.ifx_regs[DSP_YN1] = val;
|
||||
Address++;
|
||||
break;
|
||||
case 0x19: // 8-bit PCM audio
|
||||
val = DSPHost_ReadHostMemory(Address) << 8;
|
||||
g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
|
||||
g_dsp.ifx_regs[DSP_YN1] = val;
|
||||
Address++;
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(DSPLLE, "dsp_read_accelerator() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]);
|
||||
Address++;
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Take GAIN into account
|
||||
// adpcm = 0, pcm8 = 0x100, pcm16 = 0x800
|
||||
// games using pcm8 : Phoenix Wright Ace Attorney (Wiiware), Megaman 9-10 (WiiWare)
|
||||
// games using pcm16: gc sega games, ...
|
||||
|
||||
// Check for loop.
|
||||
// Somehow, YN1 and YN2 must be initialized with their "loop" values,
|
||||
// so yeah, it seems likely that we should raise an exception to let
|
||||
// the DSP program do that, at least if DSP_FORMAT == 0x0A.
|
||||
if (Address >= EndAddress)
|
||||
{
|
||||
// Set address back to start address.
|
||||
Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];
|
||||
DSPCore_SetException(EXP_ACCOV);
|
||||
}
|
||||
|
||||
g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
|
||||
g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
|
||||
return val;
|
||||
}
|
||||
26
Source/Core/Core/Src/DSP/DSPAccelerator.h
Normal file
26
Source/Core/Core/Src/DSP/DSPAccelerator.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSP_ACCELERATOR_H
|
||||
#define _DSP_ACCELERATOR_H
|
||||
|
||||
u16 dsp_read_accelerator();
|
||||
|
||||
u16 dsp_read_aram_d3();
|
||||
void dsp_write_aram_d3(u16 value);
|
||||
|
||||
#endif
|
||||
173
Source/Core/Core/Src/DSP/DSPAnalyzer.cpp
Normal file
173
Source/Core/Core/Src/DSP/DSPAnalyzer.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "DSPAnalyzer.h"
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPTables.h"
|
||||
#include "DSPMemoryMap.h"
|
||||
|
||||
namespace DSPAnalyzer {
|
||||
|
||||
// Holds data about all instructions in RAM.
|
||||
u8 code_flags[ISPACE];
|
||||
|
||||
// Good candidates for idle skipping is mail wait loops. If we're time slicing
|
||||
// between the main CPU and the DSP, if the DSP runs into one of these, it might
|
||||
// as well give up its time slice immediately, after executing once.
|
||||
|
||||
// Max signature length is 6. A 0 in a signature is ignored.
|
||||
#define NUM_IDLE_SIGS 8
|
||||
#define MAX_IDLE_SIG_SIZE 6
|
||||
|
||||
// 0xFFFF means ignore.
|
||||
const u16 idle_skip_sigs[NUM_IDLE_SIGS][MAX_IDLE_SIG_SIZE + 1] =
|
||||
{
|
||||
// From AX:
|
||||
{ 0x26fc, // LRS $30, @DMBH
|
||||
0x02c0, 0x8000, // ANDCF $30, #0x8000
|
||||
0x029d, 0xFFFF, // JLZ 0x027a
|
||||
0, 0 }, // RET
|
||||
{ 0x27fc, // LRS $31, @DMBH
|
||||
0x03c0, 0x8000, // ANDCF $31, #0x8000
|
||||
0x029d, 0xFFFF, // JLZ 0x027a
|
||||
0, 0 }, // RET
|
||||
{ 0x26fe, // LRS $30, @CMBH
|
||||
0x02c0, 0x8000, // ANDCF $30, #0x8000
|
||||
0x029c, 0xFFFF, // JLNZ 0x0280
|
||||
0, 0 }, // RET
|
||||
{ 0x27fe, // LRS $31, @CMBH
|
||||
0x03c0, 0x8000, // ANDCF $31, #0x8000
|
||||
0x029c, 0xFFFF, // JLNZ 0x0280
|
||||
0, 0 }, // RET
|
||||
{ 0x26fc, // lrs $AC0.M, @DMBH
|
||||
0x02a0, 0x8000, // andf $AC0.M, #0x8000
|
||||
0x029c, 0xFFFF, // jlnz 0x????
|
||||
0, 0 },
|
||||
{ 0x27fc, // lrs $AC1.M, @DMBH
|
||||
0x03a0, 0x8000, // andf $AC1.M, #0x8000
|
||||
0x029c, 0xFFFF, // jlnz 0x????
|
||||
0, 0 },
|
||||
// From Zelda:
|
||||
{ 0x00de, 0xFFFE, // LR $AC0.M, @CMBH
|
||||
0x02c0, 0x8000, // ANDCF $AC0.M, #0x8000
|
||||
0x029c, 0xFFFF, // JLNZ 0x05cf
|
||||
0 },
|
||||
// From Zelda - experimental
|
||||
{ 0x00da, 0x0352, // lr $AX0.H, @0x0352
|
||||
0x8600, // tstaxh $AX0.H
|
||||
0x0295, 0xFFFF, // jz 0x????
|
||||
0, 0 }
|
||||
};
|
||||
|
||||
void Reset()
|
||||
{
|
||||
memset(code_flags, 0, sizeof(code_flags));
|
||||
}
|
||||
|
||||
void AnalyzeRange(int start_addr, int end_addr)
|
||||
{
|
||||
// First we run an extremely simplified version of a disassembler to find
|
||||
// where all instructions start.
|
||||
|
||||
// This may not be 100% accurate in case of jump tables!
|
||||
// It could get desynced, which would be bad. We'll see if that's an issue.
|
||||
u16 last_arithmetic = 0;
|
||||
for (int addr = start_addr; addr < end_addr;)
|
||||
{
|
||||
UDSPInstruction inst = dsp_imem_read(addr);
|
||||
const DSPOPCTemplate *opcode = GetOpTemplate(inst);
|
||||
if (!opcode)
|
||||
{
|
||||
addr++;
|
||||
continue;
|
||||
}
|
||||
code_flags[addr] |= CODE_START_OF_INST;
|
||||
// Look for loops.
|
||||
if ((inst & 0xffe0) == 0x0060 || (inst & 0xff00) == 0x1100)
|
||||
{
|
||||
// BLOOP, BLOOPI
|
||||
u16 loop_end = dsp_imem_read(addr + 1);
|
||||
code_flags[addr] |= CODE_LOOP_START;
|
||||
code_flags[loop_end] |= CODE_LOOP_END;
|
||||
}
|
||||
else if ((inst & 0xffe0) == 0x0040 || (inst & 0xff00) == 0x1000)
|
||||
{
|
||||
// LOOP, LOOPI
|
||||
code_flags[addr] |= CODE_LOOP_START;
|
||||
code_flags[addr + 1] |= CODE_LOOP_END;
|
||||
}
|
||||
|
||||
// Mark the last arithmetic/multiplier instruction before a branch.
|
||||
// We must update the SR reg at these instructions
|
||||
if (opcode->updates_sr)
|
||||
{
|
||||
last_arithmetic = addr;
|
||||
}
|
||||
|
||||
if (opcode->branch && !opcode->uncond_branch)
|
||||
{
|
||||
code_flags[last_arithmetic] |= CODE_UPDATE_SR;
|
||||
}
|
||||
|
||||
// If an instruction potentially raises exceptions, mark the following
|
||||
// instruction as needing to check for exceptions
|
||||
if (opcode->opcode == 0x00c0 ||
|
||||
opcode->opcode == 0x1800 ||
|
||||
opcode->opcode == 0x1880 ||
|
||||
opcode->opcode == 0x1900 ||
|
||||
opcode->opcode == 0x1980 ||
|
||||
opcode->opcode == 0x2000 ||
|
||||
opcode->extended
|
||||
)
|
||||
code_flags[addr + opcode->size] |= CODE_CHECK_INT;
|
||||
|
||||
addr += opcode->size;
|
||||
}
|
||||
|
||||
// Next, we'll scan for potential idle skips.
|
||||
for (int s = 0; s < NUM_IDLE_SIGS; s++)
|
||||
{
|
||||
for (int addr = start_addr; addr < end_addr; addr++)
|
||||
{
|
||||
bool found = false;
|
||||
for (int i = 0; i < MAX_IDLE_SIG_SIZE + 1; i++)
|
||||
{
|
||||
if (idle_skip_sigs[s][i] == 0)
|
||||
found = true;
|
||||
if (idle_skip_sigs[s][i] == 0xFFFF)
|
||||
continue;
|
||||
if (idle_skip_sigs[s][i] != dsp_imem_read(addr + i))
|
||||
break;
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
INFO_LOG(DSPLLE, "Idle skip location found at %02x (sigNum:%d)", addr, s+1);
|
||||
code_flags[addr] |= CODE_IDLE_SKIP;
|
||||
}
|
||||
}
|
||||
}
|
||||
INFO_LOG(DSPLLE, "Finished analysis.");
|
||||
}
|
||||
|
||||
void Analyze()
|
||||
{
|
||||
Reset();
|
||||
AnalyzeRange(0x0000, 0x1000); // IRAM
|
||||
AnalyzeRange(0x8000, 0x9000); // IROM
|
||||
}
|
||||
|
||||
} // namespace
|
||||
52
Source/Core/Core/Src/DSP/DSPAnalyzer.h
Normal file
52
Source/Core/Core/Src/DSP/DSPAnalyzer.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Basic code analysis.
|
||||
|
||||
#include "DSPInterpreter.h"
|
||||
|
||||
namespace DSPAnalyzer {
|
||||
|
||||
#define ISPACE 65536
|
||||
|
||||
// Useful things to detect:
|
||||
// * Loop endpoints - so that we can avoid checking for loops every cycle.
|
||||
|
||||
enum
|
||||
{
|
||||
CODE_START_OF_INST = 1,
|
||||
CODE_IDLE_SKIP = 2,
|
||||
CODE_LOOP_START = 4,
|
||||
CODE_LOOP_END = 8,
|
||||
CODE_UPDATE_SR = 16,
|
||||
CODE_CHECK_INT = 32,
|
||||
};
|
||||
|
||||
// Easy to query array covering the whole of instruction memory.
|
||||
// Just index by address.
|
||||
// This one will be helpful for debuggers and jits.
|
||||
extern u8 code_flags[ISPACE];
|
||||
|
||||
// This one should be called every time IRAM changes - which is basically
|
||||
// every time that a new ucode gets uploaded, and never else. At that point,
|
||||
// we can do as much static analysis as we want - but we should always throw
|
||||
// all old analysis away. Luckily the entire address space is only 64K code
|
||||
// words and the actual code space 8K instructions in total, so we can do
|
||||
// some pretty expensive analysis if necessary.
|
||||
void Analyze();
|
||||
|
||||
} // namespace
|
||||
63
Source/Core/Core/Src/DSP/DSPBreakpoints.h
Normal file
63
Source/Core/Core/Src/DSP/DSPBreakpoints.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSP_BREAKPOINTS
|
||||
#define _DSP_BREAKPOINTS
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
// super fast breakpoints for a limited range.
|
||||
// To be used interchangably with the BreakPoints class.
|
||||
class DSPBreakpoints
|
||||
{
|
||||
public:
|
||||
DSPBreakpoints() {Clear();}
|
||||
// is address breakpoint
|
||||
bool IsAddressBreakPoint(u32 addr) {
|
||||
return b[addr] != 0;
|
||||
}
|
||||
|
||||
// AddBreakPoint
|
||||
bool Add(u32 addr, bool temp=false) {
|
||||
bool was_one = b[addr] != 0;
|
||||
if (!was_one) {
|
||||
b[addr] = temp ? 2 : 1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Remove Breakpoint
|
||||
bool Remove(u32 addr) {
|
||||
bool was_one = b[addr] != 0;
|
||||
b[addr] = 0;
|
||||
return was_one;
|
||||
}
|
||||
void Clear() {
|
||||
for (int i = 0; i < 65536; i++)
|
||||
b[i] = 0;
|
||||
}
|
||||
|
||||
void DeleteByAddress(u32 addr) {
|
||||
b[addr] = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
u8 b[65536];
|
||||
};
|
||||
|
||||
#endif
|
||||
233
Source/Core/Core/Src/DSP/DSPCodeUtil.cpp
Normal file
233
Source/Core/Core/Src/DSP/DSPCodeUtil.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
#include "FileUtil.h"
|
||||
#include "StringUtil.h"
|
||||
#include "DSPCodeUtil.h"
|
||||
#include "assemble.h"
|
||||
#include "disassemble.h"
|
||||
|
||||
|
||||
bool Assemble(const char *text, std::vector<u16> &code, bool force)
|
||||
{
|
||||
AssemblerSettings settings;
|
||||
// settings.pc = 0;
|
||||
// settings.decode_registers = false;
|
||||
// settings.decode_names = false;
|
||||
settings.force = force;
|
||||
// settings.print_tabs = false;
|
||||
// settings.ext_separator = '\'';
|
||||
|
||||
// TODO: fix the terrible api of the assembler.
|
||||
DSPAssembler assembler(settings);
|
||||
if (!assembler.Assemble(text, code)) {
|
||||
std::cerr << assembler.GetErrorString() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Disassemble(const std::vector<u16> &code, bool line_numbers, std::string &text)
|
||||
{
|
||||
if (code.empty())
|
||||
return false;
|
||||
|
||||
AssemblerSettings settings;
|
||||
|
||||
// These two prevent roundtripping.
|
||||
settings.show_hex = true;
|
||||
settings.show_pc = line_numbers;
|
||||
settings.ext_separator = '\'';
|
||||
settings.decode_names = true;
|
||||
settings.decode_registers = true;
|
||||
|
||||
DSPDisassembler disasm(settings);
|
||||
bool success = disasm.Disassemble(0, code, 0x0000, text);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Compare(const std::vector<u16> &code1, const std::vector<u16> &code2)
|
||||
{
|
||||
if (code1.size() != code2.size())
|
||||
printf("Size difference! 1=%i 2=%i\n", (int)code1.size(), (int)code2.size());
|
||||
u32 count_equal = 0;
|
||||
const int min_size = (int)std::min(code1.size(), code2.size());
|
||||
|
||||
AssemblerSettings settings;
|
||||
DSPDisassembler disassembler(settings);
|
||||
for (int i = 0; i < min_size; i++)
|
||||
{
|
||||
if (code1[i] == code2[i])
|
||||
count_equal++;
|
||||
else
|
||||
{
|
||||
std::string line1, line2;
|
||||
u16 pc = i;
|
||||
disassembler.DisOpcode(&code1[0], 0x0000, 2, &pc, line1);
|
||||
pc = i;
|
||||
disassembler.DisOpcode(&code2[0], 0x0000, 2, &pc, line2);
|
||||
printf("!! %04x : %04x vs %04x - %s vs %s\n", i, code1[i], code2[i], line1.c_str(), line2.c_str());
|
||||
}
|
||||
}
|
||||
if (code2.size() != code1.size())
|
||||
{
|
||||
printf("Extra code words:\n");
|
||||
const std::vector<u16> &longest = code1.size() > code2.size() ? code1 : code2;
|
||||
for (int i = min_size; i < (int)longest.size(); i++)
|
||||
{
|
||||
u16 pc = i;
|
||||
std::string line;
|
||||
disassembler.DisOpcode(&longest[0], 0x0000, 2, &pc, line);
|
||||
printf("!! %s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
printf("Equal instruction words: %i / %i\n", count_equal, min_size);
|
||||
return code1.size() == code2.size() && code1.size() == count_equal;
|
||||
}
|
||||
|
||||
void GenRandomCode(int size, std::vector<u16> &code)
|
||||
{
|
||||
code.resize(size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
code[i] = rand() ^ (rand() << 8);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeToHeader(const std::vector<u16> &code, std::string _filename,
|
||||
const char *name, std::string &header)
|
||||
{
|
||||
std::vector<u16> code_padded = code;
|
||||
// Pad with nops to 32byte boundary
|
||||
while (code_padded.size() & 0x7f)
|
||||
code_padded.push_back(0);
|
||||
|
||||
char buffer[1024];
|
||||
header.clear();
|
||||
header.reserve(code_padded.size() * 4);
|
||||
header.append("#define NUM_UCODES 1\n\n");
|
||||
std::string filename;
|
||||
SplitPath(_filename, NULL, &filename, NULL);
|
||||
header.append(StringFromFormat("const char* UCODE_NAMES[NUM_UCODES] = {\"%s\"};\n\n", filename.c_str()));
|
||||
header.append("const unsigned short dsp_code[NUM_UCODES][0x1000] = {\n");
|
||||
|
||||
header.append("\t{\n\t\t");
|
||||
for (u32 j = 0; j < code_padded.size(); j++)
|
||||
{
|
||||
if (j && ((j & 15) == 0))
|
||||
header.append("\n\t\t");
|
||||
sprintf(buffer, "0x%04x, ", code_padded[j]);
|
||||
header.append(buffer);
|
||||
}
|
||||
header.append("\n\t},\n");
|
||||
|
||||
header.append("};\n");
|
||||
}
|
||||
|
||||
void CodesToHeader(const std::vector<u16> *codes, const std::vector<std::string>* filenames,
|
||||
int numCodes, const char *name, std::string &header)
|
||||
{
|
||||
std::vector<std::vector<u16> > codes_padded;
|
||||
char buffer[1024];
|
||||
int reserveSize = 0;
|
||||
for(int i = 0; i < numCodes; i++)
|
||||
{
|
||||
codes_padded.push_back(codes[i]);
|
||||
// Pad with nops to 32byte boundary
|
||||
while (codes_padded.at(i).size() & 0x7f)
|
||||
codes_padded.at(i).push_back(0);
|
||||
|
||||
reserveSize += (int)codes_padded.at(i).size();
|
||||
}
|
||||
|
||||
|
||||
header.clear();
|
||||
header.reserve(reserveSize * 4);
|
||||
sprintf(buffer, "#define NUM_UCODES %d\n\n", numCodes);
|
||||
header.append(buffer);
|
||||
header.append("const char* UCODE_NAMES[NUM_UCODES] = {\n");
|
||||
for (int i = 0; i < numCodes; i++)
|
||||
{
|
||||
std::string filename;
|
||||
if (! SplitPath(filenames->at(i), NULL, &filename, NULL))
|
||||
filename = filenames->at(i);
|
||||
sprintf(buffer, "\t\"%s\",\n", filename.c_str());
|
||||
header.append(buffer);
|
||||
}
|
||||
header.append("};\n\n");
|
||||
header.append("const unsigned short dsp_code[NUM_UCODES][0x1000] = {\n");
|
||||
|
||||
for(int i = 0; i < numCodes; i++)
|
||||
{
|
||||
if(codes[i].size() == 0)
|
||||
continue;
|
||||
|
||||
header.append("\t{\n\t\t");
|
||||
for (u32 j = 0; j < codes_padded.at(i).size(); j++)
|
||||
{
|
||||
if (j && ((j & 15) == 0))
|
||||
header.append("\n\t\t");
|
||||
sprintf(buffer, "0x%04x, ", codes_padded.at(i).at(j));
|
||||
header.append(buffer);
|
||||
}
|
||||
header.append("\n\t},\n");
|
||||
}
|
||||
header.append("};\n");
|
||||
}
|
||||
|
||||
void CodeToBinaryStringBE(const std::vector<u16> &code, std::string &str)
|
||||
{
|
||||
str.resize(code.size() * 2);
|
||||
for (int i = 0; i < (int)code.size(); i++)
|
||||
{
|
||||
str[i * 2 + 0] = code[i] >> 8;
|
||||
str[i * 2 + 1] = code[i] & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void BinaryStringBEToCode(const std::string &str, std::vector<u16> &code)
|
||||
{
|
||||
code.resize(str.size() / 2);
|
||||
for (int i = 0; i < (int)code.size(); i++)
|
||||
{
|
||||
code[i] = ((u16)(u8)str[i * 2 + 0] << 8) | ((u16)(u8)str[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadBinary(const char *filename, std::vector<u16> &code)
|
||||
{
|
||||
std::string buffer;
|
||||
if (!File::ReadFileToString(false, filename, buffer))
|
||||
return false;
|
||||
|
||||
BinaryStringBEToCode(buffer, code);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveBinary(const std::vector<u16> &code, const char *filename)
|
||||
{
|
||||
std::string buffer;
|
||||
CodeToBinaryStringBE(code, buffer);
|
||||
if (!File::WriteStringToFile(false, buffer, filename))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
43
Source/Core/Core/Src/DSP/DSPCodeUtil.h
Normal file
43
Source/Core/Core/Src/DSP/DSPCodeUtil.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPCODEUTIL_H
|
||||
#define _DSPCODEUTIL_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
bool Assemble(const char *text, std::vector<u16> &code, bool force = false);
|
||||
bool Disassemble(const std::vector<u16> &code, bool line_numbers, std::string &text);
|
||||
bool Compare(const std::vector<u16> &code1, const std::vector<u16> &code2);
|
||||
void GenRandomCode(int size, std::vector<u16> &code);
|
||||
void CodeToHeader(const std::vector<u16> &code, std::string _filename,
|
||||
const char *name, std::string &header);
|
||||
void CodesToHeader(const std::vector<u16> *codes, const std::vector<std::string> *filenames,
|
||||
int numCodes, const char *name, std::string &header);
|
||||
|
||||
// Big-endian, for writing straight to file using File::WriteStringToFile.
|
||||
void CodeToBinaryStringBE(const std::vector<u16> &code, std::string &str);
|
||||
void BinaryStringBEToCode(const std::string &str, std::vector<u16> &code);
|
||||
|
||||
// Load code (big endian binary).
|
||||
bool LoadBinary(const char *filename, std::vector<u16> &code);
|
||||
bool SaveBinary(const std::vector<u16> &code, const char *filename);
|
||||
|
||||
#endif // _DSPCODEUTIL_H
|
||||
8
Source/Core/Core/Src/DSP/DSPCommon.h
Normal file
8
Source/Core/Core/Src/DSP/DSPCommon.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef __DSPCOMMON_H
|
||||
#define __DSPCOMMON_H
|
||||
#include "CommonTypes.h"
|
||||
typedef u16 UDSPInstruction;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
443
Source/Core/Core/Src/DSP/DSPCore.cpp
Normal file
443
Source/Core/Core/Src/DSP/DSPCore.cpp
Normal file
@@ -0,0 +1,443 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_interpreter.cpp
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Hash.h"
|
||||
#include "Thread.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPEmitter.h"
|
||||
#include "DSPHost.h"
|
||||
#include "DSPAnalyzer.h"
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "DSPHWInterface.h"
|
||||
#include "DSPIntUtil.h"
|
||||
|
||||
SDSP g_dsp;
|
||||
DSPBreakpoints dsp_breakpoints;
|
||||
DSPCoreState core_state = DSPCORE_STOP;
|
||||
u16 cyclesLeft = 0;
|
||||
DSPEmitter *dspjit = NULL;
|
||||
Common::Event step_event;
|
||||
Common::CriticalSection ExtIntCriticalSection;
|
||||
|
||||
static bool LoadRom(const char *fname, int size_in_words, u16 *rom)
|
||||
{
|
||||
FILE *pFile = fopen(fname, "rb");
|
||||
const size_t size_in_bytes = size_in_words * sizeof(u16);
|
||||
if (pFile)
|
||||
{
|
||||
fread(rom, 1, size_in_bytes, pFile);
|
||||
fclose(pFile);
|
||||
|
||||
// Byteswap the rom.
|
||||
for (int i = 0; i < size_in_words; i++)
|
||||
rom[i] = Common::swap16(rom[i]);
|
||||
|
||||
// Always keep ROMs write protected.
|
||||
WriteProtectMemory(rom, size_in_bytes, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
PanicAlertT("Failed to load DSP ROM: %s", fname);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns false iff the hash fails and the user hits "Yes"
|
||||
static bool VerifyRoms(const char *irom_filename, const char *coef_filename)
|
||||
{
|
||||
static const u32 hash[] = { 0x66f334fe, 0xf3b93527 };
|
||||
static const int size[] = { DSP_IROM_BYTE_SIZE, DSP_COEF_BYTE_SIZE };
|
||||
const u16 *data[] = { g_dsp.irom, g_dsp.coef };
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (HashAdler32((u8*)data[i], size[i]) != hash[i])
|
||||
{
|
||||
if (AskYesNoT("%s has an incorrect hash.\n"
|
||||
"Would you like to stop now to fix the problem?\n"
|
||||
"If you select \"No\", audio will be garbled.",
|
||||
(i == 0) ? irom_filename : coef_filename))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSPCore_Init(const char *irom_filename, const char *coef_filename,
|
||||
bool bUsingJIT)
|
||||
{
|
||||
g_dsp.step_counter = 0;
|
||||
cyclesLeft = 0;
|
||||
dspjit = NULL;
|
||||
|
||||
g_dsp.irom = (u16*)AllocateMemoryPages(DSP_IROM_BYTE_SIZE);
|
||||
g_dsp.iram = (u16*)AllocateMemoryPages(DSP_IRAM_BYTE_SIZE);
|
||||
g_dsp.dram = (u16*)AllocateMemoryPages(DSP_DRAM_BYTE_SIZE);
|
||||
g_dsp.coef = (u16*)AllocateMemoryPages(DSP_COEF_BYTE_SIZE);
|
||||
|
||||
// Fill roms with zeros.
|
||||
memset(g_dsp.irom, 0, DSP_IROM_BYTE_SIZE);
|
||||
memset(g_dsp.coef, 0, DSP_COEF_BYTE_SIZE);
|
||||
|
||||
// Try to load real ROM contents.
|
||||
LoadRom(irom_filename, DSP_IROM_SIZE, g_dsp.irom);
|
||||
LoadRom(coef_filename, DSP_COEF_SIZE, g_dsp.coef);
|
||||
if (!VerifyRoms(irom_filename, coef_filename))
|
||||
return false;
|
||||
|
||||
memset(&g_dsp.r,0,sizeof(g_dsp.r));
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
g_dsp.reg_stack_ptr[i] = 0;
|
||||
for (int j = 0; j < DSP_STACK_DEPTH; j++)
|
||||
{
|
||||
g_dsp.reg_stack[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill IRAM with HALT opcodes.
|
||||
for (int i = 0; i < DSP_IRAM_SIZE; i++)
|
||||
{
|
||||
g_dsp.iram[i] = 0x0021; // HALT opcode
|
||||
}
|
||||
|
||||
// Just zero out DRAM.
|
||||
for (int i = 0; i < DSP_DRAM_SIZE; i++)
|
||||
{
|
||||
g_dsp.dram[i] = 0;
|
||||
}
|
||||
|
||||
// Copied from a real console after the custom UCode has been loaded.
|
||||
// These are the indexing wrapping registers.
|
||||
g_dsp.r.wr[0] = 0xffff;
|
||||
g_dsp.r.wr[1] = 0xffff;
|
||||
g_dsp.r.wr[2] = 0xffff;
|
||||
g_dsp.r.wr[3] = 0xffff;
|
||||
|
||||
g_dsp.r.sr |= SR_INT_ENABLE;
|
||||
g_dsp.r.sr |= SR_EXT_INT_ENABLE;
|
||||
|
||||
g_dsp.cr = 0x804;
|
||||
gdsp_ifx_init();
|
||||
// Mostly keep IRAM write protected. We unprotect only when DMA-ing
|
||||
// in new ucodes.
|
||||
WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
||||
|
||||
// Initialize JIT, if necessary
|
||||
if(bUsingJIT)
|
||||
dspjit = new DSPEmitter();
|
||||
|
||||
DSPAnalyzer::Analyze();
|
||||
|
||||
step_event.Init();
|
||||
|
||||
core_state = DSPCORE_RUNNING;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSPCore_Shutdown()
|
||||
{
|
||||
core_state = DSPCORE_STOP;
|
||||
|
||||
if(dspjit) {
|
||||
delete dspjit;
|
||||
dspjit = NULL;
|
||||
}
|
||||
step_event.Shutdown();
|
||||
FreeMemoryPages(g_dsp.irom, DSP_IROM_BYTE_SIZE);
|
||||
FreeMemoryPages(g_dsp.iram, DSP_IRAM_BYTE_SIZE);
|
||||
FreeMemoryPages(g_dsp.dram, DSP_DRAM_BYTE_SIZE);
|
||||
FreeMemoryPages(g_dsp.coef, DSP_COEF_BYTE_SIZE);
|
||||
}
|
||||
|
||||
void DSPCore_Reset()
|
||||
{
|
||||
g_dsp.pc = DSP_RESET_VECTOR;
|
||||
|
||||
g_dsp.r.wr[0] = 0xffff;
|
||||
g_dsp.r.wr[1] = 0xffff;
|
||||
g_dsp.r.wr[2] = 0xffff;
|
||||
g_dsp.r.wr[3] = 0xffff;
|
||||
|
||||
}
|
||||
|
||||
void DSPCore_SetException(u8 level)
|
||||
{
|
||||
g_dsp.exceptions |= 1 << level;
|
||||
}
|
||||
|
||||
// Notify that an external interrupt is pending (used by thread mode)
|
||||
void DSPCore_SetExternalInterrupt(bool val)
|
||||
{
|
||||
ExtIntCriticalSection.Enter();
|
||||
g_dsp.external_interrupt_waiting = val;
|
||||
ExtIntCriticalSection.Leave();
|
||||
}
|
||||
|
||||
// Coming from the CPU
|
||||
void DSPCore_CheckExternalInterrupt()
|
||||
{
|
||||
if (! dsp_SR_is_flag_set(SR_EXT_INT_ENABLE))
|
||||
return;
|
||||
|
||||
// Signal the SPU about new mail
|
||||
DSPCore_SetException(EXP_INT);
|
||||
|
||||
g_dsp.cr &= ~CR_EXTERNAL_INT;
|
||||
}
|
||||
|
||||
|
||||
void DSPCore_CheckExceptions()
|
||||
{
|
||||
// Early out to skip the loop in the common case.
|
||||
if (g_dsp.exceptions == 0)
|
||||
return;
|
||||
|
||||
for (int i = 7; i > 0; i--) {
|
||||
// Seems exp int are not masked by sr_int_enable
|
||||
if (g_dsp.exceptions & (1 << i)) {
|
||||
if (dsp_SR_is_flag_set(SR_INT_ENABLE) || (i == EXP_INT)) {
|
||||
|
||||
// store pc and sr until RTI
|
||||
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
|
||||
dsp_reg_store_stack(DSP_STACK_D, g_dsp.r.sr);
|
||||
|
||||
g_dsp.pc = i * 2;
|
||||
g_dsp.exceptions &= ~(1 << i);
|
||||
if (i == 7)
|
||||
g_dsp.r.sr &= ~SR_EXT_INT_ENABLE;
|
||||
else
|
||||
g_dsp.r.sr &= ~SR_INT_ENABLE;
|
||||
break;
|
||||
} else {
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
ERROR_LOG(DSPLLE, "Firing exception %d failed", i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate to JIT or interpreter as appropriate.
|
||||
// Handle state changes and stepping.
|
||||
int DSPCore_RunCycles(int cycles)
|
||||
{
|
||||
if (dspjit)
|
||||
{
|
||||
cyclesLeft = cycles;
|
||||
CompiledCode pExecAddr = (CompiledCode)dspjit->enterDispatcher;
|
||||
pExecAddr();
|
||||
|
||||
if (g_dsp.external_interrupt_waiting)
|
||||
{
|
||||
DSPCore_CheckExternalInterrupt();
|
||||
DSPCore_CheckExceptions();
|
||||
DSPCore_SetExternalInterrupt(false);
|
||||
}
|
||||
|
||||
return cyclesLeft;
|
||||
}
|
||||
|
||||
while (cycles > 0)
|
||||
{
|
||||
reswitch:
|
||||
switch (core_state)
|
||||
{
|
||||
case DSPCORE_RUNNING:
|
||||
// Seems to slow things down
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
cycles = DSPInterpreter::RunCyclesDebug(cycles);
|
||||
#else
|
||||
cycles = DSPInterpreter::RunCycles(cycles);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case DSPCORE_STEPPING:
|
||||
step_event.Wait();
|
||||
if (core_state != DSPCORE_STEPPING)
|
||||
goto reswitch;
|
||||
|
||||
DSPInterpreter::Step();
|
||||
cycles--;
|
||||
|
||||
DSPHost_UpdateDebugger();
|
||||
break;
|
||||
case DSPCORE_STOP:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cycles;
|
||||
}
|
||||
|
||||
void DSPCore_SetState(DSPCoreState new_state)
|
||||
{
|
||||
core_state = new_state;
|
||||
// kick the event, in case we are waiting
|
||||
if (new_state == DSPCORE_RUNNING)
|
||||
step_event.Set();
|
||||
// Sleep(10);
|
||||
DSPHost_UpdateDebugger();
|
||||
}
|
||||
|
||||
DSPCoreState DSPCore_GetState()
|
||||
{
|
||||
return core_state;
|
||||
}
|
||||
|
||||
void DSPCore_Step()
|
||||
{
|
||||
if (core_state == DSPCORE_STEPPING)
|
||||
step_event.Set();
|
||||
}
|
||||
|
||||
void CompileCurrent()
|
||||
{
|
||||
dspjit->Compile(g_dsp.pc);
|
||||
|
||||
bool retry = true;
|
||||
|
||||
while (retry)
|
||||
{
|
||||
retry = false;
|
||||
for(u16 i = 0x0000; i < 0xffff; ++i)
|
||||
{
|
||||
if (!dspjit->unresolvedJumps[i].empty())
|
||||
{
|
||||
u16 addrToCompile = dspjit->unresolvedJumps[i].front();
|
||||
dspjit->Compile(addrToCompile);
|
||||
if (!dspjit->unresolvedJumps[i].empty())
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u16 DSPCore_ReadRegister(int reg) {
|
||||
switch(reg) {
|
||||
case DSP_REG_AR0:
|
||||
case DSP_REG_AR1:
|
||||
case DSP_REG_AR2:
|
||||
case DSP_REG_AR3:
|
||||
return g_dsp.r.ar[reg - DSP_REG_AR0];
|
||||
case DSP_REG_IX0:
|
||||
case DSP_REG_IX1:
|
||||
case DSP_REG_IX2:
|
||||
case DSP_REG_IX3:
|
||||
return g_dsp.r.ix[reg - DSP_REG_IX0];
|
||||
case DSP_REG_WR0:
|
||||
case DSP_REG_WR1:
|
||||
case DSP_REG_WR2:
|
||||
case DSP_REG_WR3:
|
||||
return g_dsp.r.wr[reg - DSP_REG_WR0];
|
||||
case DSP_REG_ST0:
|
||||
case DSP_REG_ST1:
|
||||
case DSP_REG_ST2:
|
||||
case DSP_REG_ST3:
|
||||
return g_dsp.r.st[reg - DSP_REG_ST0];
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
return g_dsp.r.ac[reg - DSP_REG_ACH0].h;
|
||||
case DSP_REG_CR: return g_dsp.r.cr;
|
||||
case DSP_REG_SR: return g_dsp.r.sr;
|
||||
case DSP_REG_PRODL: return g_dsp.r.prod.l;
|
||||
case DSP_REG_PRODM: return g_dsp.r.prod.m;
|
||||
case DSP_REG_PRODH: return g_dsp.r.prod.h;
|
||||
case DSP_REG_PRODM2: return g_dsp.r.prod.m2;
|
||||
case DSP_REG_AXL0:
|
||||
case DSP_REG_AXL1:
|
||||
return g_dsp.r.ax[reg - DSP_REG_AXL0].l;
|
||||
case DSP_REG_AXH0:
|
||||
case DSP_REG_AXH1:
|
||||
return g_dsp.r.ax[reg - DSP_REG_AXH0].h;
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
return g_dsp.r.ac[reg - DSP_REG_ACL0].l;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
return g_dsp.r.ac[reg - DSP_REG_ACM0].m;
|
||||
default:
|
||||
_assert_msg_(DSP_CORE, 0, "cannot happen");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DSPCore_WriteRegister(int reg, u16 val) {
|
||||
switch(reg) {
|
||||
case DSP_REG_AR0:
|
||||
case DSP_REG_AR1:
|
||||
case DSP_REG_AR2:
|
||||
case DSP_REG_AR3:
|
||||
g_dsp.r.ar[reg - DSP_REG_AR0] = val;
|
||||
break;
|
||||
case DSP_REG_IX0:
|
||||
case DSP_REG_IX1:
|
||||
case DSP_REG_IX2:
|
||||
case DSP_REG_IX3:
|
||||
g_dsp.r.ix[reg - DSP_REG_IX0] = val;
|
||||
break;
|
||||
case DSP_REG_WR0:
|
||||
case DSP_REG_WR1:
|
||||
case DSP_REG_WR2:
|
||||
case DSP_REG_WR3:
|
||||
g_dsp.r.wr[reg - DSP_REG_WR0] = val;
|
||||
break;
|
||||
case DSP_REG_ST0:
|
||||
case DSP_REG_ST1:
|
||||
case DSP_REG_ST2:
|
||||
case DSP_REG_ST3:
|
||||
g_dsp.r.st[reg - DSP_REG_ST0] = val;
|
||||
break;
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
g_dsp.r.ac[reg - DSP_REG_ACH0].h = val;
|
||||
break;
|
||||
case DSP_REG_CR: g_dsp.r.cr = val; break;
|
||||
case DSP_REG_SR: g_dsp.r.sr = val; break;
|
||||
case DSP_REG_PRODL: g_dsp.r.prod.l = val; break;
|
||||
case DSP_REG_PRODM: g_dsp.r.prod.m = val; break;
|
||||
case DSP_REG_PRODH: g_dsp.r.prod.h = val; break;
|
||||
case DSP_REG_PRODM2: g_dsp.r.prod.m2 = val; break;
|
||||
case DSP_REG_AXL0:
|
||||
case DSP_REG_AXL1:
|
||||
g_dsp.r.ax[reg - DSP_REG_AXL0].l = val;
|
||||
break;
|
||||
case DSP_REG_AXH0:
|
||||
case DSP_REG_AXH1:
|
||||
g_dsp.r.ax[reg - DSP_REG_AXH0].h = val;
|
||||
break;
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
g_dsp.r.ac[reg - DSP_REG_ACL0].l = val;
|
||||
break;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
g_dsp.r.ac[reg - DSP_REG_ACM0].m = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
306
Source/Core/Core/Src/DSP/DSPCore.h
Normal file
306
Source/Core/Core/Src/DSP/DSPCore.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: DSPCore.h
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#ifndef _DSPCORE_H
|
||||
#define _DSPCORE_H
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
#include "DSPBreakpoints.h"
|
||||
#include "DSPEmitter.h"
|
||||
|
||||
#define DSP_IRAM_BYTE_SIZE 0x2000
|
||||
#define DSP_IRAM_SIZE 0x1000
|
||||
#define DSP_IRAM_MASK 0x0fff
|
||||
|
||||
#define DSP_IROM_BYTE_SIZE 0x2000
|
||||
#define DSP_IROM_SIZE 0x1000
|
||||
#define DSP_IROM_MASK 0x0fff
|
||||
|
||||
#define DSP_DRAM_BYTE_SIZE 0x2000
|
||||
#define DSP_DRAM_SIZE 0x1000
|
||||
#define DSP_DRAM_MASK 0x0fff
|
||||
|
||||
#define DSP_COEF_BYTE_SIZE 0x1000
|
||||
#define DSP_COEF_SIZE 0x800
|
||||
#define DSP_COEF_MASK 0x7ff
|
||||
|
||||
#define DSP_RESET_VECTOR 0x8000
|
||||
|
||||
#define DSP_STACK_DEPTH 0x20
|
||||
#define DSP_STACK_MASK 0x1f
|
||||
|
||||
#define DSP_CR_IMEM 2
|
||||
#define DSP_CR_DMEM 0
|
||||
#define DSP_CR_TO_CPU 1
|
||||
#define DSP_CR_FROM_CPU 0
|
||||
|
||||
|
||||
// Register table taken from libasnd
|
||||
#define DSP_REG_AR0 0x00 // address registers
|
||||
#define DSP_REG_AR1 0x01
|
||||
#define DSP_REG_AR2 0x02
|
||||
#define DSP_REG_AR3 0x03
|
||||
|
||||
#define DSP_REG_IX0 0x04 // indexing registers (actually, mostly used as increments)
|
||||
#define DSP_REG_IX1 0x05
|
||||
#define DSP_REG_IX2 0x06
|
||||
#define DSP_REG_IX3 0x07
|
||||
|
||||
#define DSP_REG_WR0 0x08 // address wrapping registers. should be initialized to 0xFFFF if not used.
|
||||
#define DSP_REG_WR1 0x09
|
||||
#define DSP_REG_WR2 0x0a
|
||||
#define DSP_REG_WR3 0x0b
|
||||
|
||||
#define DSP_REG_ST0 0x0c // stacks.
|
||||
#define DSP_REG_ST1 0x0d
|
||||
#define DSP_REG_ST2 0x0e
|
||||
#define DSP_REG_ST3 0x0f
|
||||
|
||||
#define DSP_REG_CR 0x12 // Seems to be the top 8 bits of LRS/SRS.
|
||||
#define DSP_REG_SR 0x13
|
||||
|
||||
#define DSP_REG_PRODL 0x14 // product.
|
||||
#define DSP_REG_PRODM 0x15
|
||||
#define DSP_REG_PRODH 0x16
|
||||
#define DSP_REG_PRODM2 0x17
|
||||
|
||||
#define DSP_REG_AXL0 0x18
|
||||
#define DSP_REG_AXL1 0x19
|
||||
#define DSP_REG_AXH0 0x1a
|
||||
#define DSP_REG_AXH1 0x1b
|
||||
|
||||
#define DSP_REG_ACC0 0x1c // accumulator (global)
|
||||
#define DSP_REG_ACC1 0x1d
|
||||
|
||||
#define DSP_REG_ACL0 0x1c // Low accumulator
|
||||
#define DSP_REG_ACL1 0x1d
|
||||
#define DSP_REG_ACM0 0x1e // Mid accumulator
|
||||
#define DSP_REG_ACM1 0x1f
|
||||
#define DSP_REG_ACH0 0x10 // Sign extended 8 bit register 0
|
||||
#define DSP_REG_ACH1 0x11 // Sign extended 8 bit register 1
|
||||
|
||||
// Hardware registers address
|
||||
|
||||
#define DSP_COEF_A1_0 0xa0
|
||||
|
||||
#define DSP_DSCR 0xc9 // DSP DMA Control Reg
|
||||
#define DSP_DSPA 0xcd // DSP DMA Address (DSP)
|
||||
#define DSP_DSBL 0xcb // DSP DMA Block Length
|
||||
#define DSP_DSMAH 0xce // DSP DMA Address High (External)
|
||||
#define DSP_DSMAL 0xcf // DSP DMA Address Low (External)
|
||||
|
||||
#define DSP_FORMAT 0xd1 // Sample format
|
||||
#define DSP_ACUNK 0xd2 // Set to 3 on my dumps
|
||||
#define DSP_ACDATA1 0xd3 // used only by Zelda ucodes
|
||||
#define DSP_ACSAH 0xd4 // Start of loop
|
||||
#define DSP_ACSAL 0xd5
|
||||
#define DSP_ACEAH 0xd6 // End of sample (and loop)
|
||||
#define DSP_ACEAL 0xd7
|
||||
#define DSP_ACCAH 0xd8 // Current playback position
|
||||
#define DSP_ACCAL 0xd9
|
||||
#define DSP_PRED_SCALE 0xda // ADPCM predictor and scale
|
||||
#define DSP_YN1 0xdb
|
||||
#define DSP_YN2 0xdc
|
||||
#define DSP_ACCELERATOR 0xdd // ADPCM accelerator read. Used by AX.
|
||||
#define DSP_GAIN 0xde
|
||||
#define DSP_ACUNK2 0xdf // Set to 0xc on my dumps
|
||||
|
||||
#define DSP_AMDM 0xef // ARAM DMA Request Mask 0: DMA with ARAM unmasked 1: masked
|
||||
|
||||
#define DSP_DIRQ 0xfb // DSP Irq Rest
|
||||
#define DSP_DMBH 0xfc // DSP Mailbox H
|
||||
#define DSP_DMBL 0xfd // DSP Mailbox L
|
||||
#define DSP_CMBH 0xfe // CPU Mailbox H
|
||||
#define DSP_CMBL 0xff // CPU Mailbox L
|
||||
|
||||
#define DMA_TO_DSP 0
|
||||
#define DMA_TO_CPU 1
|
||||
|
||||
// Stacks
|
||||
#define DSP_STACK_C 0
|
||||
#define DSP_STACK_D 1
|
||||
|
||||
// cr (Not g_dsp.r[CR]) bits
|
||||
// See HW/DSP.cpp.
|
||||
#define CR_EXTERNAL_INT 0x0002
|
||||
#define CR_HALT 0x0004
|
||||
#define CR_INIT 0x0400
|
||||
|
||||
|
||||
// SR bits
|
||||
#define SR_CARRY 0x0001
|
||||
#define SR_OVERFLOW 0x0002
|
||||
#define SR_ARITH_ZERO 0x0004
|
||||
#define SR_SIGN 0x0008
|
||||
#define SR_OVER_S32 0x0010 // set when there there was mod/tst/cmp on accu and result is over s32
|
||||
#define SR_TOP2BITS 0x0020 // if the upper (ac?.m/ax?.h) 2 bits are equal
|
||||
#define SR_LOGIC_ZERO 0x0040
|
||||
#define SR_OVERFLOW_STICKY 0x0080 // set at the same time as 0x2 (under same conditions) - but not cleared the same
|
||||
#define SR_100 0x0100 // unknown
|
||||
#define SR_INT_ENABLE 0x0200 // Not 100% sure but duddie says so. This should replace the hack, if so.
|
||||
#define SR_400 0x0400 // unknown
|
||||
#define SR_EXT_INT_ENABLE 0x0800 // Appears in zelda - seems to disable external interupts
|
||||
#define SR_1000 0x1000 // unknown
|
||||
#define SR_MUL_MODIFY 0x2000 // 1 = normal. 0 = x2 (M0, M2)
|
||||
#define SR_40_MODE_BIT 0x4000 // 0 = "16", 1 = "40" (SET16, SET40) Controls sign extension when loading mid accums.
|
||||
#define SR_MUL_UNSIGNED 0x8000 // 0 = normal. 1 = unsigned (CLR15, SET15) If set, treats operands as unsigned. Tested with mulx only so far.
|
||||
|
||||
// This should be the bits affected by CMP. Does not include logic zero.
|
||||
#define SR_CMP_MASK 0x3f
|
||||
|
||||
// exceptions vector
|
||||
#define EXP_STOVF 1 // 0x0002 stack under/over flow
|
||||
#define EXP_2 2 // 0x0004
|
||||
#define EXP_3 3 // 0x0006
|
||||
#define EXP_4 4 // 0x0008
|
||||
#define EXP_ACCOV 5 // 0x000a accelerator address overflow
|
||||
#define EXP_6 6 // 0x000c
|
||||
#define EXP_INT 7 // 0x000e external int (message from cpu)
|
||||
|
||||
typedef struct {
|
||||
u16 ar[4];
|
||||
u16 ix[4];
|
||||
u16 wr[4];
|
||||
u16 st[4];
|
||||
u16 cr;
|
||||
u16 sr;
|
||||
union {
|
||||
u64 val;
|
||||
struct {
|
||||
u16 l;
|
||||
u16 m;
|
||||
u16 h;
|
||||
u16 m2;//if this gets in the way, drop it.
|
||||
};
|
||||
} prod;
|
||||
union {
|
||||
u32 val;
|
||||
struct {
|
||||
u16 l;
|
||||
u16 h;
|
||||
};
|
||||
} ax[2];
|
||||
union {
|
||||
u64 val;
|
||||
struct {
|
||||
u16 l;
|
||||
u16 m;
|
||||
u16 h;
|
||||
};
|
||||
} ac[2];
|
||||
} DSP_Regs;
|
||||
|
||||
// All the state of the DSP should be in this struct. Any DSP state that is not filled on init
|
||||
// should be moved here.
|
||||
struct SDSP
|
||||
{
|
||||
DSP_Regs r;
|
||||
u16 pc;
|
||||
#if PROFILE
|
||||
u16 err_pc;
|
||||
#endif
|
||||
|
||||
// This is NOT the same cr as r.cr.
|
||||
// This register is shared with the main emulation, see DSP.cpp
|
||||
// The plugin has control over 0x0C07 of this reg.
|
||||
// Bits are defined in a struct in DSP.cpp.
|
||||
u16 cr;
|
||||
|
||||
u8 reg_stack_ptr[4];
|
||||
u8 exceptions; // pending exceptions
|
||||
bool external_interrupt_waiting;
|
||||
|
||||
// DSP hardware stacks. They're mapped to a bunch of registers, such that writes
|
||||
// to them push and reads pop.
|
||||
// Let's make stack depth 32 for now, which is way more than what's needed.
|
||||
// The real DSP has different depths for the different stacks, but it would
|
||||
// be strange if any ucode relied on stack overflows since on the DSP, when
|
||||
// the stack overflows, you're screwed.
|
||||
u16 reg_stack[4][DSP_STACK_DEPTH];
|
||||
|
||||
// For debugging.
|
||||
u32 iram_crc;
|
||||
u64 step_counter;
|
||||
|
||||
// Mailbox.
|
||||
volatile u16 mbox[2][2];
|
||||
|
||||
// Mutex protecting the mailbox.
|
||||
Common::CriticalSection g_CriticalSection;
|
||||
|
||||
// Accelerator / DMA / other hardware registers. Not GPRs.
|
||||
u16 ifx_regs[256];
|
||||
|
||||
// When state saving, all of the above can just be memcpy'd into the save state.
|
||||
// The below needs special handling.
|
||||
u16 *iram;
|
||||
u16 *dram;
|
||||
u16 *irom;
|
||||
u16 *coef;
|
||||
|
||||
// This one doesn't really belong here.
|
||||
u8 *cpu_ram;
|
||||
};
|
||||
|
||||
extern SDSP g_dsp;
|
||||
extern DSPBreakpoints dsp_breakpoints;
|
||||
extern DSPEmitter *dspjit;
|
||||
extern u16 cyclesLeft;
|
||||
|
||||
bool DSPCore_Init(const char *irom_filename, const char *coef_filename,
|
||||
bool bUsingJIT);
|
||||
|
||||
void DSPCore_Reset();
|
||||
void DSPCore_Shutdown(); // Frees all allocated memory.
|
||||
|
||||
void DSPCore_CheckExternalInterrupt();
|
||||
void DSPCore_CheckExceptions();
|
||||
void DSPCore_SetExternalInterrupt(bool val);
|
||||
|
||||
// sets a flag in the pending exception register.
|
||||
void DSPCore_SetException(u8 level);
|
||||
|
||||
void CompileCurrent();
|
||||
|
||||
enum DSPCoreState
|
||||
{
|
||||
DSPCORE_STOP = 0,
|
||||
DSPCORE_RUNNING,
|
||||
DSPCORE_STEPPING,
|
||||
};
|
||||
|
||||
int DSPCore_RunCycles(int cycles);
|
||||
|
||||
// These are meant to be called from the UI thread.
|
||||
void DSPCore_SetState(DSPCoreState new_state);
|
||||
DSPCoreState DSPCore_GetState();
|
||||
|
||||
void DSPCore_Step();
|
||||
|
||||
u16 DSPCore_ReadRegister(int reg);
|
||||
void DSPCore_WriteRegister(int reg, u16 val);
|
||||
|
||||
#endif // _DSPCORE_H
|
||||
446
Source/Core/Core/Src/DSP/DSPEmitter.cpp
Normal file
446
Source/Core/Core/Src/DSP/DSPEmitter.cpp
Normal file
@@ -0,0 +1,446 @@
|
||||
// Copyright (C) 2010 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DSPEmitter.h"
|
||||
#include "DSPMemoryMap.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPAnalyzer.h"
|
||||
#include "Jit/DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
|
||||
#define MAX_BLOCK_SIZE 250
|
||||
#define DSP_IDLE_SKIP_CYCLES 0x1000
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
DSPEmitter::DSPEmitter() : gpr(*this), storeIndex(-1), storeIndex2(-1)
|
||||
{
|
||||
m_compiledCode = NULL;
|
||||
|
||||
AllocCodeSpace(COMPILED_CODE_SIZE);
|
||||
|
||||
blocks = new CompiledCode[MAX_BLOCKS];
|
||||
blockLinks = new Block[MAX_BLOCKS];
|
||||
blockSize = new u16[MAX_BLOCKS];
|
||||
unresolvedJumps = new std::list<u16>[MAX_BLOCKS];
|
||||
|
||||
compileSR = 0;
|
||||
compileSR |= SR_INT_ENABLE;
|
||||
compileSR |= SR_EXT_INT_ENABLE;
|
||||
|
||||
CompileDispatcher();
|
||||
stubEntryPoint = CompileStub();
|
||||
|
||||
//clear all of the block references
|
||||
for(int i = 0x0000; i < MAX_BLOCKS; i++)
|
||||
{
|
||||
blocks[i] = (CompiledCode)stubEntryPoint;
|
||||
blockLinks[i] = 0;
|
||||
blockSize[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DSPEmitter::~DSPEmitter()
|
||||
{
|
||||
delete[] blocks;
|
||||
delete[] blockLinks;
|
||||
delete[] blockSize;
|
||||
FreeCodeSpace();
|
||||
}
|
||||
|
||||
void DSPEmitter::ClearIRAM() {
|
||||
// ClearCodeSpace();
|
||||
for(int i = 0x0000; i < 0x1000; i++)
|
||||
{
|
||||
blocks[i] = (CompiledCode)stubEntryPoint;
|
||||
blockLinks[i] = 0;
|
||||
blockSize[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Must go out of block if exception is detected
|
||||
void DSPEmitter::checkExceptions(u32 retval)
|
||||
{
|
||||
// Check for interrupts and exceptions
|
||||
#ifdef _M_IX86 // All32
|
||||
TEST(8, M(&g_dsp.exceptions), Imm8(0xff));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&g_dsp.exceptions));
|
||||
TEST(8, MatR(RAX), Imm8(0xff));
|
||||
#endif
|
||||
FixupBranch skipCheck = J_CC(CC_Z);
|
||||
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC));
|
||||
#endif
|
||||
|
||||
DSPJitRegCache c(gpr);
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunction((void *)&DSPCore_CheckExceptions);
|
||||
MOV(32, R(EAX), Imm32(retval));
|
||||
JMP(returnDispatcher, true);
|
||||
gpr.flushRegs(c,false);
|
||||
|
||||
SetJumpTarget(skipCheck);
|
||||
}
|
||||
|
||||
void DSPEmitter::Default(UDSPInstruction inst)
|
||||
{
|
||||
if (opTable[inst]->reads_pc)
|
||||
{
|
||||
// Increment PC - we shouldn't need to do this for every instruction. only for branches and end of block.
|
||||
// Fallbacks to interpreter need this for fetching immediate values
|
||||
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 1));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Fall back to interpreter
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunctionC16((void*)opTable[inst]->intFunc, inst);
|
||||
LoadDSPRegs();
|
||||
}
|
||||
|
||||
void DSPEmitter::EmitInstruction(UDSPInstruction inst)
|
||||
{
|
||||
const DSPOPCTemplate *tinst = GetOpTemplate(inst);
|
||||
bool ext_is_jit = false;
|
||||
|
||||
// Call extended
|
||||
if (tinst->extended) {
|
||||
if ((inst >> 12) == 0x3) {
|
||||
if (! extOpTable[inst & 0x7F]->jitFunc) {
|
||||
// Fall back to interpreter
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunctionC16((void*)extOpTable[inst & 0x7F]->intFunc, inst);
|
||||
LoadDSPRegs();
|
||||
INFO_LOG(DSPLLE, "Instruction not JITed(ext part): %04x\n", inst);
|
||||
ext_is_jit = false;
|
||||
} else {
|
||||
(this->*extOpTable[inst & 0x7F]->jitFunc)(inst);
|
||||
ext_is_jit = true;
|
||||
}
|
||||
} else {
|
||||
if (!extOpTable[inst & 0xFF]->jitFunc) {
|
||||
// Fall back to interpreter
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunctionC16((void*)extOpTable[inst & 0xFF]->intFunc, inst);
|
||||
LoadDSPRegs();
|
||||
INFO_LOG(DSPLLE, "Instruction not JITed(ext part): %04x\n", inst);
|
||||
ext_is_jit = false;
|
||||
} else {
|
||||
(this->*extOpTable[inst & 0xFF]->jitFunc)(inst);
|
||||
ext_is_jit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main instruction
|
||||
if (!opTable[inst]->jitFunc) {
|
||||
Default(inst);
|
||||
INFO_LOG(DSPLLE, "Instruction not JITed(main part): %04x\n", inst);
|
||||
}
|
||||
else
|
||||
{
|
||||
(this->*opTable[inst]->jitFunc)(inst);
|
||||
}
|
||||
|
||||
// Backlog
|
||||
if (tinst->extended) {
|
||||
if (!ext_is_jit) {
|
||||
//need to call the online cleanup function because
|
||||
//the writeBackLog gets populated at runtime
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunction((void*)::applyWriteBackLog);
|
||||
LoadDSPRegs();
|
||||
} else {
|
||||
popExtValueToReg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSPEmitter::unknown_instruction(UDSPInstruction inst)
|
||||
{
|
||||
PanicAlert("unknown_instruction %04x - Fix me ;)", inst);
|
||||
}
|
||||
|
||||
void DSPEmitter::Compile(u16 start_addr)
|
||||
{
|
||||
// Remember the current block address for later
|
||||
startAddr = start_addr;
|
||||
unresolvedJumps[start_addr].clear();
|
||||
|
||||
const u8 *entryPoint = AlignCode16();
|
||||
|
||||
/*
|
||||
// Check for other exceptions
|
||||
if (dsp_SR_is_flag_set(SR_INT_ENABLE))
|
||||
return;
|
||||
|
||||
if (g_dsp.exceptions == 0)
|
||||
return;
|
||||
*/
|
||||
|
||||
LoadDSPRegs();
|
||||
|
||||
blockLinkEntry = GetCodePtr();
|
||||
|
||||
compilePC = start_addr;
|
||||
bool fixup_pc = false;
|
||||
blockSize[start_addr] = 0;
|
||||
|
||||
while (compilePC < start_addr + MAX_BLOCK_SIZE)
|
||||
{
|
||||
if (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_CHECK_INT)
|
||||
checkExceptions(blockSize[start_addr]);
|
||||
|
||||
UDSPInstruction inst = dsp_imem_read(compilePC);
|
||||
const DSPOPCTemplate *opcode = GetOpTemplate(inst);
|
||||
|
||||
EmitInstruction(inst);
|
||||
|
||||
blockSize[start_addr]++;
|
||||
compilePC += opcode->size;
|
||||
|
||||
// If the block was trying to link into itself, remove the link
|
||||
unresolvedJumps[start_addr].remove(compilePC);
|
||||
|
||||
fixup_pc = true;
|
||||
|
||||
// Handle loop condition, only if current instruction was flagged as a loop destination
|
||||
// by the analyzer.
|
||||
if (DSPAnalyzer::code_flags[compilePC-1] & DSPAnalyzer::CODE_LOOP_END)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
MOVZX(32, 16, EAX, M(&(g_dsp.r.st[2])));
|
||||
#else
|
||||
MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
MOVZX(32, 16, EAX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[2])));
|
||||
#endif
|
||||
CMP(32, R(EAX), Imm32(0));
|
||||
FixupBranch rLoopAddressExit = J_CC(CC_LE, true);
|
||||
|
||||
#ifdef _M_IX86 // All32
|
||||
MOVZX(32, 16, EAX, M(&g_dsp.r.st[3]));
|
||||
#else
|
||||
MOVZX(32, 16, EAX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[3])));
|
||||
#endif
|
||||
CMP(32, R(EAX), Imm32(0));
|
||||
FixupBranch rLoopCounterExit = J_CC(CC_LE, true);
|
||||
|
||||
if (!opcode->branch)
|
||||
{
|
||||
//branch insns update the g_dsp.pc
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC));
|
||||
#endif
|
||||
}
|
||||
|
||||
// These functions branch and therefore only need to be called in the
|
||||
// end of each block and in this order
|
||||
DSPJitRegCache c(gpr);
|
||||
HandleLoop();
|
||||
SaveDSPRegs();
|
||||
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
|
||||
{
|
||||
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
|
||||
}
|
||||
JMP(returnDispatcher, true);
|
||||
gpr.flushRegs(c,false);
|
||||
|
||||
SetJumpTarget(rLoopAddressExit);
|
||||
SetJumpTarget(rLoopCounterExit);
|
||||
}
|
||||
|
||||
if (opcode->branch)
|
||||
{
|
||||
//don't update g_dsp.pc -- the branch insn already did
|
||||
fixup_pc = false;
|
||||
if (opcode->uncond_branch)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (!opcode->jitFunc)
|
||||
{
|
||||
//look at g_dsp.pc if we actually branched
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, R(AX), M(&g_dsp.pc));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, R(AX), MatR(RAX));
|
||||
#endif
|
||||
CMP(16, R(AX), Imm16(compilePC));
|
||||
FixupBranch rNoBranch = J_CC(CC_Z);
|
||||
|
||||
DSPJitRegCache c(gpr);
|
||||
//don't update g_dsp.pc -- the branch insn already did
|
||||
SaveDSPRegs();
|
||||
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
|
||||
{
|
||||
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
|
||||
}
|
||||
JMP(returnDispatcher, true);
|
||||
gpr.flushRegs(c,false);
|
||||
|
||||
SetJumpTarget(rNoBranch);
|
||||
}
|
||||
}
|
||||
|
||||
// End the block if we're before an idle skip address
|
||||
if (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_IDLE_SKIP)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fixup_pc) {
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC));
|
||||
#endif
|
||||
}
|
||||
|
||||
blocks[start_addr] = (CompiledCode)entryPoint;
|
||||
|
||||
// Mark this block as a linkable destination if it does not contain
|
||||
// any unresolved CALL's
|
||||
if (unresolvedJumps[start_addr].empty())
|
||||
{
|
||||
blockLinks[start_addr] = blockLinkEntry;
|
||||
|
||||
for(u16 i = 0x0000; i < 0xffff; ++i)
|
||||
{
|
||||
if (!unresolvedJumps[i].empty())
|
||||
{
|
||||
// Check if there were any blocks waiting for this block to be linkable
|
||||
unsigned int size = unresolvedJumps[i].size();
|
||||
unresolvedJumps[i].remove(start_addr);
|
||||
if (unresolvedJumps[i].size() < size)
|
||||
{
|
||||
// Mark the block to be recompiled again
|
||||
blocks[i] = (CompiledCode)stubEntryPoint;
|
||||
blockLinks[i] = 0;
|
||||
blockSize[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blockSize[start_addr] == 0)
|
||||
{
|
||||
// just a safeguard, should never happen anymore.
|
||||
// if it does we might get stuck over in RunForCycles.
|
||||
ERROR_LOG(DSPLLE, "Block at 0x%04x has zero size", start_addr);
|
||||
blockSize[start_addr] = 1;
|
||||
}
|
||||
|
||||
SaveDSPRegs();
|
||||
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
|
||||
{
|
||||
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
|
||||
}
|
||||
JMP(returnDispatcher, true);
|
||||
}
|
||||
|
||||
const u8 *DSPEmitter::CompileStub()
|
||||
{
|
||||
const u8 *entryPoint = AlignCode16();
|
||||
ABI_CallFunction((void *)&CompileCurrent);
|
||||
//MOVZX(32, 16, ECX, M(&g_dsp.pc));
|
||||
XOR(32, R(EAX), R(EAX)); // Return 0 cycles executed
|
||||
JMP(returnDispatcher);
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
void DSPEmitter::CompileDispatcher()
|
||||
{
|
||||
enterDispatcher = AlignCode16();
|
||||
ABI_PushAllCalleeSavedRegsAndAdjustStack();
|
||||
|
||||
const u8 *dispatcherLoop = GetCodePtr();
|
||||
|
||||
// Check for DSP halt
|
||||
#ifdef _M_IX86
|
||||
TEST(8, M(&g_dsp.cr), Imm8(CR_HALT));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&g_dsp.cr));
|
||||
TEST(8, MatR(RAX), Imm8(CR_HALT));
|
||||
#endif
|
||||
FixupBranch _halt = J_CC(CC_NE);
|
||||
|
||||
#ifdef _M_IX86
|
||||
MOVZX(32, 16, ECX, M(&g_dsp.pc));
|
||||
#else
|
||||
MOV(64, R(RCX), ImmPtr(&g_dsp.pc));
|
||||
MOVZX(64, 16, RCX, MatR(RCX));
|
||||
#endif
|
||||
|
||||
// Execute block. Cycles executed returned in EAX.
|
||||
#ifdef _M_IX86
|
||||
MOV(32, R(EBX), ImmPtr(blocks));
|
||||
JMPptr(MComplex(EBX, ECX, SCALE_4, 0));
|
||||
#else
|
||||
MOV(64, R(RBX), ImmPtr(blocks));
|
||||
JMPptr(MComplex(RBX, RCX, SCALE_8, 0));
|
||||
#endif
|
||||
|
||||
returnDispatcher = GetCodePtr();
|
||||
|
||||
// Decrement cyclesLeft
|
||||
#ifdef _M_IX86
|
||||
SUB(16, M(&cyclesLeft), R(EAX));
|
||||
#else
|
||||
MOV(64, R(R12), ImmPtr(&cyclesLeft));
|
||||
SUB(16, MatR(R12), R(EAX));
|
||||
#endif
|
||||
|
||||
J_CC(CC_A, dispatcherLoop);
|
||||
|
||||
// DSP gave up the remaining cycles.
|
||||
SetJumpTarget(_halt);
|
||||
//MOV(32, M(&cyclesLeft), Imm32(0));
|
||||
ABI_PopAllCalleeSavedRegsAndAdjustStack();
|
||||
RET();
|
||||
}
|
||||
303
Source/Core/Core/Src/DSP/DSPEmitter.h
Normal file
303
Source/Core/Core/Src/DSP/DSPEmitter.h
Normal file
@@ -0,0 +1,303 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPEMITTER_H
|
||||
#define _DSPEMITTER_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "DSPCommon.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "Jit/DSPJitRegCache.h"
|
||||
|
||||
#define COMPILED_CODE_SIZE sizeof(void *) * 0x200000
|
||||
|
||||
#define MAX_BLOCKS 0x10000
|
||||
|
||||
typedef u32 (*CompiledCode)();
|
||||
typedef const u8 *Block;
|
||||
|
||||
class DSPEmitter : public Gen::XCodeBlock, NonCopyable
|
||||
{
|
||||
public:
|
||||
DSPEmitter();
|
||||
~DSPEmitter();
|
||||
|
||||
Block m_compiledCode;
|
||||
|
||||
void EmitInstruction(UDSPInstruction inst);
|
||||
void unknown_instruction(UDSPInstruction inst);
|
||||
void ClearIRAM();
|
||||
|
||||
void CompileDispatcher();
|
||||
Block CompileStub();
|
||||
void Compile(u16 start_addr);
|
||||
void ClearCallFlag();
|
||||
|
||||
void Default(UDSPInstruction inst);
|
||||
|
||||
// CC Util
|
||||
void Update_SR_Register64(Gen::X64Reg val = Gen::EAX);
|
||||
void Update_SR_Register64_Carry(Gen::X64Reg val = Gen::EAX);
|
||||
void Update_SR_Register64_Carry2(Gen::X64Reg val = Gen::EAX);
|
||||
void Update_SR_Register16(Gen::X64Reg val = Gen::EAX);
|
||||
void Update_SR_Register16_OverS32(Gen::X64Reg val = Gen::EAX);
|
||||
|
||||
// Register helpers
|
||||
void setCompileSR(u16 bit);
|
||||
void clrCompileSR(u16 bit);
|
||||
void checkExceptions(u32 retval);
|
||||
|
||||
// Memory helper functions
|
||||
void increment_addr_reg(int reg);
|
||||
void decrement_addr_reg(int reg);
|
||||
void increase_addr_reg(int reg);
|
||||
void decrease_addr_reg(int reg);
|
||||
void imem_read();
|
||||
void dmem_read();
|
||||
void dmem_read_imm(u16 addr);
|
||||
void dmem_write();
|
||||
void dmem_write_imm(u16 addr);
|
||||
|
||||
// Ext command helpers
|
||||
void pushExtValueFromReg(u16 dreg, u16 sreg);
|
||||
void popExtValueToReg();
|
||||
void pushExtValueFromMem(u16 dreg, u16 sreg);
|
||||
void pushExtValueFromMem2(u16 dreg, u16 sreg);
|
||||
void zeroWriteBackLog(const UDSPInstruction opc);
|
||||
|
||||
// Ext commands
|
||||
void l(const UDSPInstruction opc);
|
||||
void ln(const UDSPInstruction opc);
|
||||
void ls(const UDSPInstruction opc);
|
||||
void lsn(const UDSPInstruction opc);
|
||||
void lsm(const UDSPInstruction opc);
|
||||
void lsnm(const UDSPInstruction opc);
|
||||
void sl(const UDSPInstruction opc);
|
||||
void sln(const UDSPInstruction opc);
|
||||
void slm(const UDSPInstruction opc);
|
||||
void slnm(const UDSPInstruction opc);
|
||||
void s(const UDSPInstruction opc);
|
||||
void sn(const UDSPInstruction opc);
|
||||
void ld(const UDSPInstruction opc);
|
||||
void ldn(const UDSPInstruction opc);
|
||||
void ldm(const UDSPInstruction opc);
|
||||
void ldnm(const UDSPInstruction opc);
|
||||
void mv(const UDSPInstruction opc);
|
||||
void dr(const UDSPInstruction opc);
|
||||
void ir(const UDSPInstruction opc);
|
||||
void nr(const UDSPInstruction opc);
|
||||
void nop(const UDSPInstruction opc) {}
|
||||
|
||||
// Command helpers
|
||||
void dsp_reg_stack_push(int stack_reg);
|
||||
void dsp_reg_stack_pop(int stack_reg);
|
||||
void dsp_reg_store_stack(int stack_reg, Gen::X64Reg host_sreg = Gen::EDX);
|
||||
void dsp_reg_load_stack(int stack_reg, Gen::X64Reg host_dreg = Gen::EDX);
|
||||
void dsp_reg_store_stack_imm(int stack_reg, u16 val);
|
||||
void dsp_op_write_reg(int reg, Gen::X64Reg host_sreg);
|
||||
void dsp_op_write_reg_imm(int reg, u16 val);
|
||||
void dsp_conditional_extend_accum(int reg);
|
||||
void dsp_conditional_extend_accum_imm(int reg, u16 val);
|
||||
void dsp_op_read_reg(int reg, Gen::X64Reg host_dreg, DSPJitSignExtend extend = NONE);
|
||||
|
||||
// Commands
|
||||
void dar(const UDSPInstruction opc);
|
||||
void iar(const UDSPInstruction opc);
|
||||
void subarn(const UDSPInstruction opc);
|
||||
void addarn(const UDSPInstruction opc);
|
||||
void sbclr(const UDSPInstruction opc);
|
||||
void sbset(const UDSPInstruction opc);
|
||||
void srbith(const UDSPInstruction opc);
|
||||
void lri(const UDSPInstruction opc);
|
||||
void lris(const UDSPInstruction opc);
|
||||
void mrr(const UDSPInstruction opc);
|
||||
void nx(const UDSPInstruction opc);
|
||||
|
||||
// Branch
|
||||
void HandleLoop();
|
||||
void jcc(const UDSPInstruction opc);
|
||||
void jmprcc(const UDSPInstruction opc);
|
||||
void call(const UDSPInstruction opc);
|
||||
void callr(const UDSPInstruction opc);
|
||||
void ifcc(const UDSPInstruction opc);
|
||||
void ret(const UDSPInstruction opc);
|
||||
void rti(const UDSPInstruction opc);
|
||||
void halt(const UDSPInstruction opc);
|
||||
void loop(const UDSPInstruction opc);
|
||||
void loopi(const UDSPInstruction opc);
|
||||
void bloop(const UDSPInstruction opc);
|
||||
void bloopi(const UDSPInstruction opc);
|
||||
|
||||
// Load/Store
|
||||
void srs(const UDSPInstruction opc);
|
||||
void lrs(const UDSPInstruction opc);
|
||||
void lr(const UDSPInstruction opc);
|
||||
void sr(const UDSPInstruction opc);
|
||||
void si(const UDSPInstruction opc);
|
||||
void lrr(const UDSPInstruction opc);
|
||||
void lrrd(const UDSPInstruction opc);
|
||||
void lrri(const UDSPInstruction opc);
|
||||
void lrrn(const UDSPInstruction opc);
|
||||
void srr(const UDSPInstruction opc);
|
||||
void srrd(const UDSPInstruction opc);
|
||||
void srri(const UDSPInstruction opc);
|
||||
void srrn(const UDSPInstruction opc);
|
||||
void ilrr(const UDSPInstruction opc);
|
||||
void ilrrd(const UDSPInstruction opc);
|
||||
void ilrri(const UDSPInstruction opc);
|
||||
void ilrrn(const UDSPInstruction opc);
|
||||
|
||||
// Arithmetic
|
||||
void clr(const UDSPInstruction opc);
|
||||
void clrl(const UDSPInstruction opc);
|
||||
void andcf(const UDSPInstruction opc);
|
||||
void andf(const UDSPInstruction opc);
|
||||
void tst(const UDSPInstruction opc);
|
||||
void tstaxh(const UDSPInstruction opc);
|
||||
void cmp(const UDSPInstruction opc);
|
||||
void cmpar(const UDSPInstruction opc);
|
||||
void cmpi(const UDSPInstruction opc);
|
||||
void cmpis(const UDSPInstruction opc);
|
||||
void xorr(const UDSPInstruction opc);
|
||||
void andr(const UDSPInstruction opc);
|
||||
void orr(const UDSPInstruction opc);
|
||||
void andc(const UDSPInstruction opc);
|
||||
void orc(const UDSPInstruction opc);
|
||||
void xorc(const UDSPInstruction opc);
|
||||
void notc(const UDSPInstruction opc);
|
||||
void xori(const UDSPInstruction opc);
|
||||
void andi(const UDSPInstruction opc);
|
||||
void ori(const UDSPInstruction opc);
|
||||
void addr(const UDSPInstruction opc);
|
||||
void addax(const UDSPInstruction opc);
|
||||
void add(const UDSPInstruction opc);
|
||||
void addp(const UDSPInstruction opc);
|
||||
void addaxl(const UDSPInstruction opc);
|
||||
void addi(const UDSPInstruction opc);
|
||||
void addis(const UDSPInstruction opc);
|
||||
void incm(const UDSPInstruction opc);
|
||||
void inc(const UDSPInstruction opc);
|
||||
void subr(const UDSPInstruction opc);
|
||||
void subax(const UDSPInstruction opc);
|
||||
void sub(const UDSPInstruction opc);
|
||||
void subp(const UDSPInstruction opc);
|
||||
void decm(const UDSPInstruction opc);
|
||||
void dec(const UDSPInstruction opc);
|
||||
void neg(const UDSPInstruction opc);
|
||||
void abs(const UDSPInstruction opc);
|
||||
void movr(const UDSPInstruction opc);
|
||||
void movax(const UDSPInstruction opc);
|
||||
void mov(const UDSPInstruction opc);
|
||||
void lsl16(const UDSPInstruction opc);
|
||||
void lsr16(const UDSPInstruction opc);
|
||||
void asr16(const UDSPInstruction opc);
|
||||
void lsl(const UDSPInstruction opc);
|
||||
void lsr(const UDSPInstruction opc);
|
||||
void asl(const UDSPInstruction opc);
|
||||
void asr(const UDSPInstruction opc);
|
||||
void lsrn(const UDSPInstruction opc);
|
||||
void asrn(const UDSPInstruction opc);
|
||||
void lsrnrx(const UDSPInstruction opc);
|
||||
void asrnrx(const UDSPInstruction opc);
|
||||
void lsrnr(const UDSPInstruction opc);
|
||||
void asrnr(const UDSPInstruction opc);
|
||||
|
||||
// Multipliers
|
||||
void multiply();
|
||||
void multiply_add();
|
||||
void multiply_sub();
|
||||
void multiply_mulx(u8 axh0, u8 axh1);
|
||||
void clrp(const UDSPInstruction opc);
|
||||
void tstprod(const UDSPInstruction opc);
|
||||
void movp(const UDSPInstruction opc);
|
||||
void movnp(const UDSPInstruction opc);
|
||||
void movpz(const UDSPInstruction opc);
|
||||
void addpaxz(const UDSPInstruction opc);
|
||||
void mulaxh(const UDSPInstruction opc);
|
||||
void mul(const UDSPInstruction opc);
|
||||
void mulac(const UDSPInstruction opc);
|
||||
void mulmv(const UDSPInstruction opc);
|
||||
void mulmvz(const UDSPInstruction opc);
|
||||
void mulx(const UDSPInstruction opc);
|
||||
void mulxac(const UDSPInstruction opc);
|
||||
void mulxmv(const UDSPInstruction opc);
|
||||
void mulxmvz(const UDSPInstruction opc);
|
||||
void mulc(const UDSPInstruction opc);
|
||||
void mulcac(const UDSPInstruction opc);
|
||||
void mulcmv(const UDSPInstruction opc);
|
||||
void mulcmvz(const UDSPInstruction opc);
|
||||
void maddx(const UDSPInstruction opc);
|
||||
void msubx(const UDSPInstruction opc);
|
||||
void maddc(const UDSPInstruction opc);
|
||||
void msubc(const UDSPInstruction opc);
|
||||
void madd(const UDSPInstruction opc);
|
||||
void msub(const UDSPInstruction opc);
|
||||
|
||||
// CALL this to start the dispatcher
|
||||
const u8 *enterDispatcher;
|
||||
const u8 *stubEntryPoint;
|
||||
const u8 *returnDispatcher;
|
||||
u16 compilePC;
|
||||
u16 startAddr;
|
||||
Block *blockLinks;
|
||||
u16 *blockSize;
|
||||
std::list<u16> *unresolvedJumps;
|
||||
|
||||
DSPJitRegCache gpr;
|
||||
|
||||
void LoadDSPRegs();
|
||||
void SaveDSPRegs();
|
||||
|
||||
private:
|
||||
CompiledCode *blocks;
|
||||
Block blockLinkEntry;
|
||||
u16 compileSR;
|
||||
|
||||
// The index of the last stored ext value (compile time).
|
||||
int storeIndex;
|
||||
int storeIndex2;
|
||||
|
||||
// Counts down.
|
||||
// int cycles;
|
||||
|
||||
|
||||
void Update_SR_Register(Gen::X64Reg val = Gen::EAX);
|
||||
|
||||
void ToMask(Gen::X64Reg value_reg = Gen::EDI);
|
||||
void dsp_increment_one(Gen::X64Reg ar = Gen::EAX, Gen::X64Reg wr = Gen::EDX, Gen::X64Reg wr_pow = Gen::EDI, Gen::X64Reg temp_reg = Gen::ESI);
|
||||
void dsp_decrement_one(Gen::X64Reg ar = Gen::EAX, Gen::X64Reg wr = Gen::EDX, Gen::X64Reg wr_pow = Gen::EDI, Gen::X64Reg temp_reg = Gen::ESI);
|
||||
void get_long_prod(Gen::X64Reg long_prod = Gen::RAX);
|
||||
void get_long_prod_round_prodl(Gen::X64Reg long_prod = Gen::RAX);
|
||||
void set_long_prod();
|
||||
void round_long_acc(Gen::X64Reg long_acc = Gen::EAX);
|
||||
void set_long_acc(int _reg, Gen::X64Reg acc = Gen::EAX);
|
||||
void get_acc_h(int _reg, Gen::X64Reg acc = Gen::EAX, bool sign = true);
|
||||
void set_acc_h(int _reg, Gen::OpArg arg = R(Gen::EAX));
|
||||
void get_acc_m(int _reg, Gen::X64Reg acc = Gen::EAX, bool sign = true);
|
||||
void set_acc_m(int _reg, Gen::OpArg arg = R(Gen::EAX));
|
||||
void get_acc_l(int _reg, Gen::X64Reg acc = Gen::EAX, bool sign = true);
|
||||
void set_acc_l(int _reg, Gen::OpArg arg = R(Gen::EAX));
|
||||
void get_long_acx(int _reg, Gen::X64Reg acx = Gen::EAX);
|
||||
void get_ax_l(int _reg, Gen::X64Reg acx = Gen::EAX);
|
||||
void get_ax_h(int _reg, Gen::X64Reg acc = Gen::EAX);
|
||||
void get_long_acc(int _reg, Gen::X64Reg acc = Gen::EAX);
|
||||
};
|
||||
|
||||
|
||||
#endif // _DSPEMITTER_H
|
||||
|
||||
361
Source/Core/Core/Src/DSP/DSPHWInterface.cpp
Normal file
361
Source/Core/Core/Src/DSP/DSPHWInterface.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_interface.h
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#include "Thread.h"
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "DSPCore.h"
|
||||
#include "DSPHost.h"
|
||||
#include "DSPTables.h"
|
||||
#include "DSPAnalyzer.h"
|
||||
#include "DSPAccelerator.h"
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPHWInterface.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
#if _M_SSE >= 0x301 && !(defined __GNUC__ && !defined __SSSE3__)
|
||||
#include <tmmintrin.h>
|
||||
#endif
|
||||
|
||||
void gdsp_do_dma();
|
||||
|
||||
Common::CriticalSection g_CriticalSection;
|
||||
|
||||
void gdsp_ifx_init()
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
g_dsp.ifx_regs[i] = 0;
|
||||
}
|
||||
|
||||
g_dsp.mbox[0][0] = 0;
|
||||
g_dsp.mbox[0][1] = 0;
|
||||
g_dsp.mbox[1][0] = 0;
|
||||
g_dsp.mbox[1][1] = 0;
|
||||
}
|
||||
|
||||
|
||||
u32 gdsp_mbox_peek(u8 mbx)
|
||||
{
|
||||
if (DSPHost_OnThread())
|
||||
g_CriticalSection.Enter();
|
||||
u32 value = ((g_dsp.mbox[mbx][0] << 16) | g_dsp.mbox[mbx][1]);
|
||||
if (DSPHost_OnThread())
|
||||
g_CriticalSection.Leave();
|
||||
return value;
|
||||
}
|
||||
|
||||
void gdsp_mbox_write_h(u8 mbx, u16 val)
|
||||
{
|
||||
if (DSPHost_OnThread())
|
||||
g_CriticalSection.Enter();
|
||||
g_dsp.mbox[mbx][0] = val & 0x7fff;
|
||||
if (DSPHost_OnThread())
|
||||
g_CriticalSection.Leave();
|
||||
}
|
||||
|
||||
|
||||
void gdsp_mbox_write_l(u8 mbx, u16 val)
|
||||
{
|
||||
if (DSPHost_OnThread())
|
||||
g_CriticalSection.Enter();
|
||||
g_dsp.mbox[mbx][1] = val;
|
||||
g_dsp.mbox[mbx][0] |= 0x8000;
|
||||
if (DSPHost_OnThread())
|
||||
g_CriticalSection.Leave();
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
if (mbx == GDSP_MBOX_DSP)
|
||||
{
|
||||
NOTICE_LOG(DSP_MAIL, "DSP(WM) B:%i M:0x%08x (pc=0x%04x)", mbx, gdsp_mbox_peek(GDSP_MBOX_DSP), g_dsp.pc);
|
||||
} else {
|
||||
NOTICE_LOG(DSP_MAIL, "CPU(WM) B:%i M:0x%08x (pc=0x%04x)", mbx, gdsp_mbox_peek(GDSP_MBOX_CPU), g_dsp.pc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
u16 gdsp_mbox_read_h(u8 mbx)
|
||||
{
|
||||
return g_dsp.mbox[mbx][0]; // TODO: mask away the top bit?
|
||||
}
|
||||
|
||||
|
||||
u16 gdsp_mbox_read_l(u8 mbx)
|
||||
{
|
||||
if (DSPHost_OnThread())
|
||||
g_CriticalSection.Enter();
|
||||
|
||||
u16 val = g_dsp.mbox[mbx][1];
|
||||
g_dsp.mbox[mbx][0] &= ~0x8000;
|
||||
|
||||
|
||||
if (DSPHost_OnThread())
|
||||
g_CriticalSection.Leave();
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
if (mbx == GDSP_MBOX_DSP)
|
||||
{
|
||||
NOTICE_LOG(DSP_MAIL, "DSP(RM) B:%i M:0x%08x (pc=0x%04x)", mbx, gdsp_mbox_peek(GDSP_MBOX_DSP), g_dsp.pc);
|
||||
} else {
|
||||
NOTICE_LOG(DSP_MAIL, "CPU(RM) B:%i M:0x%08x (pc=0x%04x)", mbx, gdsp_mbox_peek(GDSP_MBOX_CPU), g_dsp.pc);
|
||||
}
|
||||
#endif
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void gdsp_ifx_write(u32 addr, u32 val)
|
||||
{
|
||||
switch (addr & 0xff)
|
||||
{
|
||||
case DSP_DIRQ:
|
||||
if (val & 0x1)
|
||||
DSPHost_InterruptRequest();
|
||||
else
|
||||
INFO_LOG(DSPLLE, "Unknown Interrupt Request pc=%04x (%04x)", g_dsp.pc, val);
|
||||
break;
|
||||
|
||||
case DSP_DMBH:
|
||||
gdsp_mbox_write_h(GDSP_MBOX_DSP, val);
|
||||
break;
|
||||
|
||||
case DSP_DMBL:
|
||||
gdsp_mbox_write_l(GDSP_MBOX_DSP, val);
|
||||
break;
|
||||
|
||||
case DSP_CMBH:
|
||||
return gdsp_mbox_write_h(GDSP_MBOX_CPU, val);
|
||||
|
||||
case DSP_CMBL:
|
||||
return gdsp_mbox_write_l(GDSP_MBOX_CPU, val);
|
||||
|
||||
case DSP_DSBL:
|
||||
g_dsp.ifx_regs[DSP_DSBL] = val;
|
||||
g_dsp.ifx_regs[DSP_DSCR] |= 4; // Doesn't really matter since we do DMA instantly
|
||||
if (!g_dsp.ifx_regs[DSP_AMDM])
|
||||
gdsp_do_dma();
|
||||
else
|
||||
NOTICE_LOG(DSPLLE, "Masked DMA skipped");
|
||||
g_dsp.ifx_regs[DSP_DSCR] &= ~4;
|
||||
g_dsp.ifx_regs[DSP_DSBL] = 0;
|
||||
break;
|
||||
|
||||
case DSP_ACDATA1: // Accelerator write (Zelda type) - "UnkZelda"
|
||||
dsp_write_aram_d3(val);
|
||||
break;
|
||||
|
||||
case DSP_GAIN:
|
||||
if (val) {
|
||||
INFO_LOG(DSPLLE,"Gain Written: 0x%04x", val);
|
||||
}
|
||||
case DSP_DSPA:
|
||||
case DSP_DSMAH:
|
||||
case DSP_DSMAL:
|
||||
case DSP_DSCR:
|
||||
g_dsp.ifx_regs[addr & 0xFF] = val;
|
||||
break;
|
||||
/*
|
||||
case DSP_ACCAL:
|
||||
dsp_step_accelerator();
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
if ((addr & 0xff) >= 0xa0) {
|
||||
if (pdlabels[(addr & 0xFF) - 0xa0].name && pdlabels[(addr & 0xFF) - 0xa0].description) {
|
||||
INFO_LOG(DSPLLE, "%04x MW %s (%04x)", g_dsp.pc, pdlabels[(addr & 0xFF) - 0xa0].name, val);
|
||||
}
|
||||
else {
|
||||
ERROR_LOG(DSPLLE, "%04x MW %04x (%04x)", g_dsp.pc, addr, val);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ERROR_LOG(DSPLLE, "%04x MW %04x (%04x)", g_dsp.pc, addr, val);
|
||||
}
|
||||
g_dsp.ifx_regs[addr & 0xFF] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u16 gdsp_ifx_read(u16 addr)
|
||||
{
|
||||
switch (addr & 0xff)
|
||||
{
|
||||
case DSP_DMBH:
|
||||
return gdsp_mbox_read_h(GDSP_MBOX_DSP);
|
||||
|
||||
case DSP_DMBL:
|
||||
return gdsp_mbox_read_l(GDSP_MBOX_DSP);
|
||||
|
||||
case DSP_CMBH:
|
||||
return gdsp_mbox_read_h(GDSP_MBOX_CPU);
|
||||
|
||||
case DSP_CMBL:
|
||||
return gdsp_mbox_read_l(GDSP_MBOX_CPU);
|
||||
|
||||
case DSP_DSCR:
|
||||
return g_dsp.ifx_regs[addr & 0xFF];
|
||||
|
||||
case DSP_ACCELERATOR: // ADPCM Accelerator reads
|
||||
return dsp_read_accelerator();
|
||||
|
||||
case DSP_ACDATA1: // Accelerator reads (Zelda type) - "UnkZelda"
|
||||
return dsp_read_aram_d3();
|
||||
|
||||
default:
|
||||
if ((addr & 0xff) >= 0xa0) {
|
||||
if (pdlabels[(addr & 0xFF) - 0xa0].name && pdlabels[(addr & 0xFF) - 0xa0].description) {
|
||||
INFO_LOG(DSPLLE, "%04x MR %s (%04x)", g_dsp.pc, pdlabels[(addr & 0xFF) - 0xa0].name, g_dsp.ifx_regs[addr & 0xFF]);
|
||||
}
|
||||
else {
|
||||
ERROR_LOG(DSPLLE, "%04x MR %04x (%04x)", g_dsp.pc, addr, g_dsp.ifx_regs[addr & 0xFF]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ERROR_LOG(DSPLLE, "%04x MR %04x (%04x)", g_dsp.pc, addr, g_dsp.ifx_regs[addr & 0xFF]);
|
||||
}
|
||||
return g_dsp.ifx_regs[addr & 0xFF];
|
||||
}
|
||||
}
|
||||
|
||||
void gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size)
|
||||
{
|
||||
UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
||||
|
||||
u8* dst = ((u8*)g_dsp.iram);
|
||||
for (u32 i = 0; i < size; i += 2)
|
||||
{
|
||||
// TODO : this may be different on Wii.
|
||||
*(u16*)&dst[dsp_addr + i] = Common::swap16(*(const u16*)&g_dsp.cpu_ram[(addr + i) & 0x0fffffff]);
|
||||
}
|
||||
WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
||||
|
||||
g_dsp.iram_crc = DSPHost_CodeLoaded(g_dsp.cpu_ram + (addr & 0x0fffffff), size);
|
||||
|
||||
NOTICE_LOG(DSPLLE, "*** Copy new UCode from 0x%08x to 0x%04x (crc: %8x)", addr, dsp_addr, g_dsp.iram_crc);
|
||||
|
||||
if (dspjit)
|
||||
dspjit->ClearIRAM();
|
||||
|
||||
DSPAnalyzer::Analyze();
|
||||
}
|
||||
|
||||
|
||||
void gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size)
|
||||
{
|
||||
ERROR_LOG(DSPLLE, "*** idma_out IRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
|
||||
}
|
||||
|
||||
static const __m128i s_mask = _mm_set_epi32(0x0E0F0C0DL, 0x0A0B0809L, 0x06070405L, 0x02030001L);
|
||||
|
||||
// TODO: These should eat clock cycles.
|
||||
void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
|
||||
{
|
||||
u8* dst = ((u8*)g_dsp.dram);
|
||||
|
||||
#if _M_SSE >= 0x301
|
||||
if (cpu_info.bSSSE3 && !(size % 16))
|
||||
{
|
||||
for (u32 i = 0; i < size; i += 16)
|
||||
{
|
||||
_mm_storeu_si128((__m128i *)&dst[dsp_addr + i], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&g_dsp.cpu_ram[(addr + i) & 0x7FFFFFFF]), s_mask));
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (u32 i = 0; i < size; i += 2)
|
||||
{
|
||||
*(u16*)&dst[dsp_addr + i] = Common::swap16(*(const u16*)&g_dsp.cpu_ram[(addr + i) & 0x7FFFFFFF]);
|
||||
}
|
||||
}
|
||||
INFO_LOG(DSPLLE, "*** ddma_in RAM (0x%08x) -> DRAM_DSP (0x%04x) : size (0x%08x)", addr, dsp_addr / 2, size);
|
||||
}
|
||||
|
||||
|
||||
void gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size)
|
||||
{
|
||||
const u8* src = ((const u8*)g_dsp.dram);
|
||||
|
||||
#if _M_SSE >= 0x301
|
||||
if (cpu_info.bSSSE3 && !(size % 16))
|
||||
{
|
||||
for (u32 i = 0; i < size; i += 16)
|
||||
{
|
||||
_mm_storeu_si128((__m128i *)&g_dsp.cpu_ram[(addr + i) & 0x7FFFFFFF], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&src[dsp_addr + i]), s_mask));
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (u32 i = 0; i < size; i += 2)
|
||||
{
|
||||
*(u16*)&g_dsp.cpu_ram[(addr + i) & 0x7FFFFFFF] = Common::swap16(*(const u16*)&src[dsp_addr + i]);
|
||||
}
|
||||
}
|
||||
|
||||
INFO_LOG(DSPLLE, "*** ddma_out DRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
|
||||
}
|
||||
|
||||
void gdsp_do_dma()
|
||||
{
|
||||
u16 ctl;
|
||||
u32 addr;
|
||||
u16 dsp_addr;
|
||||
u16 len;
|
||||
|
||||
addr = (g_dsp.ifx_regs[DSP_DSMAH] << 16) | g_dsp.ifx_regs[DSP_DSMAL];
|
||||
ctl = g_dsp.ifx_regs[DSP_DSCR];
|
||||
dsp_addr = g_dsp.ifx_regs[DSP_DSPA] * 2;
|
||||
len = g_dsp.ifx_regs[DSP_DSBL];
|
||||
|
||||
if (len > 0x4000)
|
||||
{
|
||||
ERROR_LOG(DSPLLE, "DMA ERROR pc: %04x ctl: %04x addr: %08x da: %04x size: %04x", g_dsp.pc, ctl, addr, dsp_addr, len);
|
||||
exit(0);
|
||||
}
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
DEBUG_LOG(DSPLLE, "DMA pc: %04x ctl: %04x addr: %08x da: %04x size: %04x", g_dsp.pc, ctl, addr, dsp_addr, len);
|
||||
#endif
|
||||
switch (ctl & 0x3)
|
||||
{
|
||||
case (DSP_CR_DMEM | DSP_CR_TO_CPU):
|
||||
gdsp_ddma_out(dsp_addr, addr, len);
|
||||
break;
|
||||
|
||||
case (DSP_CR_DMEM | DSP_CR_FROM_CPU):
|
||||
gdsp_ddma_in(dsp_addr, addr, len);
|
||||
break;
|
||||
|
||||
case (DSP_CR_IMEM | DSP_CR_TO_CPU):
|
||||
gdsp_idma_out(dsp_addr, addr, len);
|
||||
break;
|
||||
|
||||
case (DSP_CR_IMEM | DSP_CR_FROM_CPU):
|
||||
gdsp_idma_in(dsp_addr, addr, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
47
Source/Core/Core/Src/DSP/DSPHWInterface.h
Normal file
47
Source/Core/Core/Src/DSP/DSPHWInterface.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_interface.h
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#ifndef _GDSP_INTERFACE_H
|
||||
#define _GDSP_INTERFACE_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#define GDSP_MBOX_CPU 0
|
||||
#define GDSP_MBOX_DSP 1
|
||||
|
||||
u32 gdsp_mbox_peek(u8 mbx);
|
||||
void gdsp_mbox_write_h(u8 mbx, u16 val);
|
||||
void gdsp_mbox_write_l(u8 mbx, u16 val);
|
||||
u16 gdsp_mbox_read_h(u8 mbx);
|
||||
u16 gdsp_mbox_read_l(u8 mbx);
|
||||
|
||||
void gdsp_ifx_init();
|
||||
|
||||
void gdsp_ifx_write(u32 addr, u32 val);
|
||||
u16 gdsp_ifx_read(u16 addr);
|
||||
|
||||
void gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size);
|
||||
|
||||
#endif
|
||||
34
Source/Core/Core/Src/DSP/DSPHost.h
Normal file
34
Source/Core/Core/Src/DSP/DSPHost.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPHOST_H
|
||||
#define _DSPHOST_H
|
||||
|
||||
// The user of the DSPCore library must supply a few functions so that the
|
||||
// emulation core can access the environment it runs in. If the emulation
|
||||
// core isn't used, for example in an asm/disasm tool, then most of these
|
||||
// can be stubbed out.
|
||||
|
||||
u8 DSPHost_ReadHostMemory(u32 addr);
|
||||
void DSPHost_WriteHostMemory(u8 value, u32 addr);
|
||||
bool DSPHost_OnThread();
|
||||
bool DSPHost_Running();
|
||||
void DSPHost_InterruptRequest();
|
||||
u32 DSPHost_CodeLoaded(const u8 *ptr, int size);
|
||||
void DSPHost_UpdateDebugger();
|
||||
|
||||
#endif
|
||||
197
Source/Core/Core/Src/DSP/DSPIntCCUtil.cpp
Normal file
197
Source/Core/Core/Src/DSP/DSPIntCCUtil.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
|
||||
#include "DSPIntCCUtil.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPInterpreter.h"
|
||||
|
||||
namespace DSPInterpreter {
|
||||
|
||||
void Update_SR_Register64(s64 _Value, bool carry, bool overflow)
|
||||
{
|
||||
g_dsp.r.sr &= ~SR_CMP_MASK;
|
||||
|
||||
// 0x01
|
||||
if (carry)
|
||||
{
|
||||
g_dsp.r.sr |= SR_CARRY;
|
||||
}
|
||||
|
||||
// 0x02 and 0x80
|
||||
if (overflow)
|
||||
{
|
||||
g_dsp.r.sr |= SR_OVERFLOW;
|
||||
g_dsp.r.sr |= SR_OVERFLOW_STICKY;
|
||||
}
|
||||
|
||||
// 0x04
|
||||
if (_Value == 0)
|
||||
{
|
||||
g_dsp.r.sr |= SR_ARITH_ZERO;
|
||||
}
|
||||
|
||||
// 0x08
|
||||
if (_Value < 0)
|
||||
{
|
||||
g_dsp.r.sr |= SR_SIGN;
|
||||
}
|
||||
|
||||
// 0x10
|
||||
if (_Value != (s32)_Value)
|
||||
{
|
||||
g_dsp.r.sr |= SR_OVER_S32;
|
||||
}
|
||||
|
||||
// 0x20 - Checks if top bits of m are equal
|
||||
if (((_Value & 0xc0000000) == 0) || ((_Value & 0xc0000000) == 0xc0000000))
|
||||
{
|
||||
g_dsp.r.sr |= SR_TOP2BITS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Update_SR_Register16(s16 _Value, bool carry, bool overflow, bool overS32)
|
||||
{
|
||||
g_dsp.r.sr &= ~SR_CMP_MASK;
|
||||
|
||||
// 0x01
|
||||
if (carry)
|
||||
{
|
||||
g_dsp.r.sr |= SR_CARRY;
|
||||
}
|
||||
|
||||
// 0x02 and 0x80
|
||||
if (overflow)
|
||||
{
|
||||
g_dsp.r.sr |= SR_OVERFLOW;
|
||||
g_dsp.r.sr |= SR_OVERFLOW_STICKY;
|
||||
}
|
||||
|
||||
// 0x04
|
||||
if (_Value == 0)
|
||||
{
|
||||
g_dsp.r.sr |= SR_ARITH_ZERO;
|
||||
}
|
||||
|
||||
// 0x08
|
||||
if (_Value < 0)
|
||||
{
|
||||
g_dsp.r.sr |= SR_SIGN;
|
||||
}
|
||||
|
||||
// 0x10
|
||||
if (overS32)
|
||||
{
|
||||
g_dsp.r.sr |= SR_OVER_S32;
|
||||
}
|
||||
|
||||
// 0x20 - Checks if top bits of m are equal
|
||||
if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
|
||||
{
|
||||
g_dsp.r.sr |= SR_TOP2BITS;
|
||||
}
|
||||
}
|
||||
|
||||
void Update_SR_LZ(bool value)
|
||||
{
|
||||
if (value == true)
|
||||
g_dsp.r.sr |= SR_LOGIC_ZERO;
|
||||
else
|
||||
g_dsp.r.sr &= ~SR_LOGIC_ZERO;
|
||||
}
|
||||
|
||||
inline int GetMultiplyModifier()
|
||||
{
|
||||
return (g_dsp.r.sr & SR_MUL_MODIFY)?1:2;
|
||||
}
|
||||
|
||||
inline bool isCarry() {
|
||||
return (g_dsp.r.sr & SR_CARRY) ? true : false;
|
||||
}
|
||||
|
||||
inline bool isOverflow() {
|
||||
return (g_dsp.r.sr & SR_OVERFLOW) ? true : false;
|
||||
}
|
||||
|
||||
inline bool isOverS32() {
|
||||
return (g_dsp.r.sr & SR_OVER_S32) ? true : false;
|
||||
}
|
||||
|
||||
inline bool isLess() {
|
||||
return (!(g_dsp.r.sr & SR_OVERFLOW) != !(g_dsp.r.sr & SR_SIGN));
|
||||
}
|
||||
|
||||
inline bool isZero() {
|
||||
return (g_dsp.r.sr & SR_ARITH_ZERO) ? true : false;
|
||||
}
|
||||
|
||||
inline bool isLogicZero() {
|
||||
return (g_dsp.r.sr & SR_LOGIC_ZERO) ? true : false;
|
||||
}
|
||||
|
||||
inline bool isConditionA() {
|
||||
return (((g_dsp.r.sr & SR_OVER_S32) || (g_dsp.r.sr & SR_TOP2BITS)) && !(g_dsp.r.sr & SR_ARITH_ZERO)) ? true : false;
|
||||
}
|
||||
|
||||
//see DSPCore.h for flags
|
||||
bool CheckCondition(u8 _Condition)
|
||||
{
|
||||
switch (_Condition & 0xf)
|
||||
{
|
||||
case 0xf: // Always true.
|
||||
return true;
|
||||
case 0x0: // GE - Greater Equal
|
||||
return !isLess();
|
||||
case 0x1: // L - Less
|
||||
return isLess();
|
||||
case 0x2: // G - Greater
|
||||
return !isLess() && !isZero();
|
||||
case 0x3: // LE - Less Equal
|
||||
return isLess() || isZero();
|
||||
case 0x4: // NZ - Not Zero
|
||||
return !isZero();
|
||||
case 0x5: // Z - Zero
|
||||
return isZero();
|
||||
case 0x6: // NC - Not carry
|
||||
return !isCarry();
|
||||
case 0x7: // C - Carry
|
||||
return isCarry();
|
||||
case 0x8: // ? - Not over s32
|
||||
return !isOverS32();
|
||||
case 0x9: // ? - Over s32
|
||||
return isOverS32();
|
||||
case 0xa: // ?
|
||||
return isConditionA();
|
||||
case 0xb: // ?
|
||||
return !isConditionA();
|
||||
case 0xc: // LNZ - Logic Not Zero
|
||||
return !isLogicZero();
|
||||
case 0xd: // LZ - Logic Zero
|
||||
return isLogicZero();
|
||||
case 0xe: // 0 - Overflow
|
||||
return isOverflow();
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
55
Source/Core/Core/Src/DSP/DSPIntCCUtil.h
Normal file
55
Source/Core/Core/Src/DSP/DSPIntCCUtil.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
#ifndef _GDSP_CONDITION_CODES_H
|
||||
#define _GDSP_CONDITION_CODES_H
|
||||
|
||||
// Anything to do with SR and conditions goes here.
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
namespace DSPInterpreter {
|
||||
|
||||
bool CheckCondition(u8 _Condition);
|
||||
|
||||
int GetMultiplyModifier();
|
||||
|
||||
void Update_SR_Register16(s16 _Value, bool carry = false, bool overflow = false, bool overS32 = false);
|
||||
void Update_SR_Register64(s64 _Value, bool carry = false, bool overflow = false);
|
||||
void Update_SR_LZ(bool value);
|
||||
|
||||
inline bool isCarry(u64 val, u64 result) {
|
||||
return (val>result);
|
||||
}
|
||||
|
||||
inline bool isCarry2(u64 val, u64 result) {
|
||||
return (val>=result);
|
||||
}
|
||||
|
||||
inline bool isOverflow(s64 val1, s64 val2, s64 res) {
|
||||
return ((val1 ^ res) & (val2 ^ res)) < 0;
|
||||
}
|
||||
|
||||
inline bool isOverS32(s64 acc) {
|
||||
return (acc != (s32)acc) ? true : false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // _GDSP_CONDITION_CODES_H
|
||||
526
Source/Core/Core/Src/DSP/DSPIntExtOps.cpp
Normal file
526
Source/Core/Core/Src/DSP/DSPIntExtOps.cpp
Normal file
@@ -0,0 +1,526 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#include "DSPIntUtil.h"
|
||||
#include "DSPMemoryMap.h"
|
||||
#include "DSPIntExtOps.h"
|
||||
|
||||
// Extended opcodes do not exist on their own. These opcodes can only be
|
||||
// attached to opcodes that allow extending (8 (or 7) lower bits of opcode not used by
|
||||
// opcode). Extended opcodes do not modify program counter $pc register.
|
||||
|
||||
// Most of the suffixes increment or decrement one or more addressing registers
|
||||
// (the first four, ARx). The increment/decrement is either 1, or the
|
||||
// corresponding "index" register (the second four, IXx). The addressing
|
||||
// registers will wrap in odd ways, dictated by the corresponding wrapping
|
||||
// register, WR0-3.
|
||||
|
||||
// Needs comments.
|
||||
inline static void writeToBackLog(int i, int idx, u16 value)
|
||||
{
|
||||
writeBackLog[i] = value;
|
||||
writeBackLogIdx[i] = idx;
|
||||
}
|
||||
|
||||
namespace DSPInterpreter
|
||||
{
|
||||
|
||||
namespace Ext
|
||||
{
|
||||
|
||||
inline bool IsSameMemArea(u16 a, u16 b)
|
||||
{
|
||||
//LM: tested on WII
|
||||
if ((a>>10)==(b>>10))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// DR $arR
|
||||
// xxxx xxxx 0000 01rr
|
||||
// Decrement addressing register $arR.
|
||||
void dr(const UDSPInstruction opc) {
|
||||
writeToBackLog(0, opc & 0x3, dsp_decrement_addr_reg(opc & 0x3));
|
||||
}
|
||||
|
||||
// IR $arR
|
||||
// xxxx xxxx 0000 10rr
|
||||
// Increment addressing register $arR.
|
||||
void ir(const UDSPInstruction opc) {
|
||||
writeToBackLog(0, opc & 0x3, dsp_increment_addr_reg(opc & 0x3));
|
||||
}
|
||||
|
||||
// NR $arR
|
||||
// xxxx xxxx 0000 11rr
|
||||
// Add corresponding indexing register $ixR to addressing register $arR.
|
||||
void nr(const UDSPInstruction opc) {
|
||||
u8 reg = opc & 0x3;
|
||||
|
||||
writeToBackLog(0, reg, dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]));
|
||||
}
|
||||
|
||||
// MV $axD.D, $acS.S
|
||||
// xxxx xxxx 0001 ddss
|
||||
// Move value of $acS.S to the $axD.D.
|
||||
void mv(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
|
||||
u8 dreg = ((opc >> 2) & 0x3);
|
||||
|
||||
switch(sreg) {
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
writeToBackLog(0, dreg + DSP_REG_AXL0, g_dsp.r.ac[sreg-DSP_REG_ACL0].l);
|
||||
break;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
writeToBackLog(0, dreg + DSP_REG_AXL0, g_dsp.r.ac[sreg-DSP_REG_ACM0].m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// S @$arD, $acS.S
|
||||
// xxxx xxxx 001s s0dd
|
||||
// Store value of $acS.S in the memory pointed by register $arD.
|
||||
// Post increment register $arD.
|
||||
void s(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = opc & 0x3;
|
||||
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
|
||||
|
||||
switch(sreg) {
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg-DSP_REG_ACL0].l);
|
||||
break;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg-DSP_REG_ACM0].m);
|
||||
break;
|
||||
}
|
||||
writeToBackLog(0, dreg, dsp_increment_addr_reg(dreg));
|
||||
}
|
||||
|
||||
// SN @$arD, $acS.S
|
||||
// xxxx xxxx 001s s1dd
|
||||
// Store value of register $acS.S in the memory pointed by register $arD.
|
||||
// Add indexing register $ixD to register $arD.
|
||||
void sn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = opc & 0x3;
|
||||
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
|
||||
|
||||
switch(sreg) {
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg-DSP_REG_ACL0].l);
|
||||
break;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg-DSP_REG_ACM0].m);
|
||||
break;
|
||||
}
|
||||
writeToBackLog(0, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
|
||||
}
|
||||
|
||||
// L $axD.D, @$arS
|
||||
// xxxx xxxx 01dd d0ss
|
||||
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
|
||||
// Post increment register $arS.
|
||||
void l(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x3;
|
||||
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
|
||||
|
||||
if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
|
||||
{
|
||||
u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
|
||||
writeToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
|
||||
writeToBackLog(1, dreg, val);
|
||||
writeToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
|
||||
writeToBackLog(3, sreg, dsp_increment_addr_reg(sreg));
|
||||
}
|
||||
else
|
||||
{
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
writeToBackLog(1, sreg, dsp_increment_addr_reg(sreg));
|
||||
}
|
||||
}
|
||||
|
||||
// LN $axD.D, @$arS
|
||||
// xxxx xxxx 01dd d0ss
|
||||
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
|
||||
// Add indexing register register $ixS to register $arS.
|
||||
void ln(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x3;
|
||||
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
|
||||
|
||||
if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
|
||||
{
|
||||
u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
|
||||
writeToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
|
||||
writeToBackLog(1, dreg, val);
|
||||
writeToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
|
||||
writeToBackLog(3, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
|
||||
}
|
||||
else
|
||||
{
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
writeToBackLog(1, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
|
||||
}
|
||||
}
|
||||
|
||||
// LS $axD.D, $acS.m108
|
||||
// xxxx xxxx 10dd 000s
|
||||
// Load register $axD.D with value from memory pointed by register
|
||||
// $ar0. Store value from register $acS.m to memory location pointed by
|
||||
// register $ar3. Increment both $ar0 and $ar3.
|
||||
void ls(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
|
||||
dsp_dmem_write(g_dsp.r.ar[3], g_dsp.r.ac[sreg].m);
|
||||
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
|
||||
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
|
||||
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
|
||||
}
|
||||
|
||||
|
||||
// LSN $axD.D, $acS.m
|
||||
// xxxx xxxx 10dd 010s
|
||||
// Load register $axD.D with value from memory pointed by register
|
||||
// $ar0. Store value from register $acS.m to memory location pointed by
|
||||
// register $ar3. Add corresponding indexing register $ix0 to addressing
|
||||
// register $ar0 and increment $ar3.
|
||||
void lsn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
|
||||
dsp_dmem_write(g_dsp.r.ar[3], g_dsp.r.ac[sreg].m);
|
||||
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
|
||||
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
|
||||
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
|
||||
}
|
||||
|
||||
// LSM $axD.D, $acS.m
|
||||
// xxxx xxxx 10dd 100s
|
||||
// Load register $axD.D with value from memory pointed by register
|
||||
// $ar0. Store value from register $acS.m to memory location pointed by
|
||||
// register $ar3. Add corresponding indexing register $ix3 to addressing
|
||||
// register $ar3 and increment $ar0.
|
||||
void lsm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
|
||||
dsp_dmem_write(g_dsp.r.ar[3], g_dsp.r.ac[sreg].m);
|
||||
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
|
||||
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
|
||||
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
|
||||
}
|
||||
|
||||
// LSMN $axD.D, $acS.m
|
||||
// xxxx xxxx 10dd 110s
|
||||
// Load register $axD.D with value from memory pointed by register
|
||||
// $ar0. Store value from register $acS.m to memory location pointed by
|
||||
// register $ar3. Add corresponding indexing register $ix0 to addressing
|
||||
// register $ar0 and add corresponding indexing register $ix3 to addressing
|
||||
// register $ar3.
|
||||
void lsnm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
|
||||
dsp_dmem_write(g_dsp.r.ar[3], g_dsp.r.ac[sreg].m);
|
||||
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
|
||||
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
|
||||
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
|
||||
}
|
||||
|
||||
// SL $acS.m, $axD.D
|
||||
// xxxx xxxx 10dd 001s
|
||||
// Store value from register $acS.m to memory location pointed by register
|
||||
// $ar0. Load register $axD.D with value from memory pointed by register
|
||||
// $ar3. Increment both $ar0 and $ar3.
|
||||
void sl(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
|
||||
dsp_dmem_write(g_dsp.r.ar[0], g_dsp.r.ac[sreg].m);
|
||||
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
|
||||
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
|
||||
}
|
||||
|
||||
// SLN $acS.m, $axD.D
|
||||
// xxxx xxxx 10dd 011s
|
||||
// Store value from register $acS.m to memory location pointed by register
|
||||
// $ar0. Load register $axD.D with value from memory pointed by register
|
||||
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
|
||||
// and increment $ar3.
|
||||
void sln(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
|
||||
dsp_dmem_write(g_dsp.r.ar[0], g_dsp.r.ac[sreg].m);
|
||||
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
|
||||
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
|
||||
}
|
||||
|
||||
// SLM $acS.m, $axD.D
|
||||
// xxxx xxxx 10dd 101s
|
||||
// Store value from register $acS.m to memory location pointed by register
|
||||
// $ar0. Load register $axD.D with value from memory pointed by register
|
||||
// $ar3. Add corresponding indexing register $ix3 to addressing register $ar3
|
||||
// and increment $ar0.
|
||||
void slm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
|
||||
dsp_dmem_write(g_dsp.r.ar[0], g_dsp.r.ac[sreg].m);
|
||||
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
|
||||
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
|
||||
}
|
||||
|
||||
// SLMN $acS.m, $axD.D
|
||||
// xxxx xxxx 10dd 111s
|
||||
// Store value from register $acS.m to memory location pointed by register
|
||||
// $ar0. Load register $axD.D with value from memory pointed by register
|
||||
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
|
||||
// and add corresponding indexing register $ix3 to addressing register $ar3.
|
||||
void slnm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
|
||||
dsp_dmem_write(g_dsp.r.ar[0], g_dsp.r.ac[sreg].m);
|
||||
|
||||
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
|
||||
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
|
||||
}
|
||||
|
||||
// LD $ax0.d, $ax1.r, @$arS
|
||||
// xxxx xxxx 11dr 00ss
|
||||
// example for "nx'ld $AX0.L, $AX1.L, @$AR3"
|
||||
// Loads the word pointed by AR0 to AX0.H, then loads the word pointed by AR3 to AX0.L.
|
||||
// Increments AR0 and AR3.
|
||||
// If AR0 and AR3 point into the same memory page (upper 6 bits of addr are the same -> games are not doing that!)
|
||||
// then the value pointed by AR0 is loaded to BOTH AX0.H and AX0.L.
|
||||
// If AR0 points into an invalid memory page (ie 0x2000), then AX0.H keeps its old value. (not implemented yet)
|
||||
// If AR3 points into an invalid memory page, then AX0.L gets the same value as AX0.H. (not implemented yet)
|
||||
void ld(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x1;
|
||||
u8 rreg = (opc >> 4) & 0x1;
|
||||
u8 sreg = opc & 0x3;
|
||||
|
||||
if (sreg != DSP_REG_AR3) {
|
||||
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
|
||||
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
|
||||
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
else
|
||||
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
|
||||
writeToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
|
||||
} else {
|
||||
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[dreg]));
|
||||
|
||||
if (IsSameMemArea(g_dsp.r.ar[dreg], g_dsp.r.ar[3]))
|
||||
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[dreg]));
|
||||
else
|
||||
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
|
||||
writeToBackLog(2, dreg, dsp_increment_addr_reg(dreg));
|
||||
}
|
||||
|
||||
writeToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
|
||||
}
|
||||
|
||||
// LDN $ax0.d, $ax1.r, @$arS
|
||||
// xxxx xxxx 11dr 01ss
|
||||
void ldn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x1;
|
||||
u8 rreg = (opc >> 4) & 0x1;
|
||||
u8 sreg = opc & 0x3;
|
||||
|
||||
if (sreg != DSP_REG_AR3) {
|
||||
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
|
||||
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
|
||||
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
else
|
||||
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
|
||||
writeToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
|
||||
} else {
|
||||
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[dreg]));
|
||||
|
||||
if (IsSameMemArea(g_dsp.r.ar[dreg], g_dsp.r.ar[3]))
|
||||
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[dreg]));
|
||||
else
|
||||
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
|
||||
writeToBackLog(2, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
|
||||
}
|
||||
|
||||
writeToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
|
||||
}
|
||||
|
||||
// LDM $ax0.d, $ax1.r, @$arS
|
||||
// xxxx xxxx 11dr 10ss
|
||||
void ldm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x1;
|
||||
u8 rreg = (opc >> 4) & 0x1;
|
||||
u8 sreg = opc & 0x3;
|
||||
|
||||
if (sreg != DSP_REG_AR3) {
|
||||
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
|
||||
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
|
||||
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
else
|
||||
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
|
||||
writeToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
|
||||
} else {
|
||||
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[dreg]));
|
||||
|
||||
if (IsSameMemArea(g_dsp.r.ar[dreg], g_dsp.r.ar[3]))
|
||||
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[dreg]));
|
||||
else
|
||||
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
|
||||
writeToBackLog(2, dreg, dsp_increment_addr_reg(dreg));
|
||||
}
|
||||
|
||||
writeToBackLog(3, DSP_REG_AR3,
|
||||
dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
|
||||
}
|
||||
|
||||
// LDNM $ax0.d, $ax1.r, @$arS
|
||||
// xxxx xxxx 11dr 11ss
|
||||
void ldnm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x1;
|
||||
u8 rreg = (opc >> 4) & 0x1;
|
||||
u8 sreg = opc & 0x3;
|
||||
|
||||
if (sreg != DSP_REG_AR3) {
|
||||
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
|
||||
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
|
||||
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
|
||||
else
|
||||
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
|
||||
writeToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
|
||||
} else {
|
||||
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[dreg]));
|
||||
|
||||
if (IsSameMemArea(g_dsp.r.ar[dreg], g_dsp.r.ar[3]))
|
||||
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[dreg]));
|
||||
else
|
||||
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
|
||||
|
||||
writeToBackLog(2, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
|
||||
}
|
||||
|
||||
writeToBackLog(3, DSP_REG_AR3,
|
||||
dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
|
||||
}
|
||||
|
||||
|
||||
void nop(const UDSPInstruction opc)
|
||||
{
|
||||
}
|
||||
|
||||
} // end namespace ext
|
||||
} // end namespace DSPInterpeter
|
||||
|
||||
|
||||
// The ext ops are calculated in parallel with the actual op. That means that
|
||||
// both the main op and the ext op see the same register state as input. The
|
||||
// output is simple as long as the main and ext ops don't change the same
|
||||
// register. If they do the output is the bitwise or of the result of both the
|
||||
// main and ext ops.
|
||||
|
||||
// The ext op are writing their output into the backlog which is
|
||||
// being applied to the real registers after the main op was executed
|
||||
void applyWriteBackLog()
|
||||
{
|
||||
// always make sure to have an extra entry at the end w/ -1 to avoid
|
||||
// infinitive loops
|
||||
for (int i = 0; writeBackLogIdx[i] != -1; i++) {
|
||||
dsp_op_write_reg(writeBackLogIdx[i], dsp_op_read_reg(writeBackLogIdx[i]) | writeBackLog[i]);
|
||||
// Clear back log
|
||||
writeBackLogIdx[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// This function is being called in the main op after all input regs were read
|
||||
// and before it writes into any regs. This way we can always use bitwise or to
|
||||
// apply the ext command output, because if the main op didn't change the value
|
||||
// then 0 | ext output = ext output and if it did then bitwise or is still the
|
||||
// right thing to do
|
||||
void zeroWriteBackLog()
|
||||
{
|
||||
// always make sure to have an extra entry at the end w/ -1 to avoid
|
||||
// infinitive loops
|
||||
for (int i = 0; writeBackLogIdx[i] != -1; i++) {
|
||||
dsp_op_write_reg(writeBackLogIdx[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
//needed for 0x3... cases when main and extended are modifying the same ACC
|
||||
//games are not doing that + in motorola (similar dsp) dox this is forbidden to do
|
||||
//ex. corner case -> 0x3060: main opcode modifies .m, and extended .l -> .l shoudnt be zeroed because of .m write...
|
||||
void zeroWriteBackLogPreserveAcc(u8 acc)
|
||||
{
|
||||
for (int i = 0; writeBackLogIdx[i] != -1; i++) {
|
||||
|
||||
// acc0
|
||||
if ((acc == 0) &&
|
||||
((writeBackLogIdx[i] == DSP_REG_ACL0) || (writeBackLogIdx[i] == DSP_REG_ACM0) || (writeBackLogIdx[i] == DSP_REG_ACH0)))
|
||||
continue;
|
||||
|
||||
// acc1
|
||||
if ((acc == 1) &&
|
||||
((writeBackLogIdx[i] == DSP_REG_ACL1) || (writeBackLogIdx[i] == DSP_REG_ACM1) || (writeBackLogIdx[i] == DSP_REG_ACH1)))
|
||||
continue;
|
||||
|
||||
dsp_op_write_reg(writeBackLogIdx[i], 0);
|
||||
}
|
||||
}
|
||||
64
Source/Core/Core/Src/DSP/DSPIntExtOps.h
Normal file
64
Source/Core/Core/Src/DSP/DSPIntExtOps.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: opcodes.h
|
||||
project: GameCube DSP Tool (gcdsp)
|
||||
created: 2005.03.04
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#ifndef _GDSP_EXT_OP_H
|
||||
#define _GDSP_EXT_OP_H
|
||||
|
||||
#include "DSPTables.h"
|
||||
|
||||
// Extended opcode support.
|
||||
// Many opcode have the lower 0xFF (some only 0x7f) free - there, an opcode extension
|
||||
// can be stored.
|
||||
|
||||
namespace DSPInterpreter
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
void l(const UDSPInstruction opc);
|
||||
void ln(const UDSPInstruction opc);
|
||||
void ls(const UDSPInstruction opc);
|
||||
void lsn(const UDSPInstruction opc);
|
||||
void lsm(const UDSPInstruction opc);
|
||||
void lsnm(const UDSPInstruction opc);
|
||||
void sl(const UDSPInstruction opc);
|
||||
void sln(const UDSPInstruction opc);
|
||||
void slm(const UDSPInstruction opc);
|
||||
void slnm(const UDSPInstruction opc);
|
||||
void s(const UDSPInstruction opc);
|
||||
void sn(const UDSPInstruction opc);
|
||||
void ld(const UDSPInstruction opc);
|
||||
void ldn(const UDSPInstruction opc);
|
||||
void ldm(const UDSPInstruction opc);
|
||||
void ldnm(const UDSPInstruction opc);
|
||||
void mv(const UDSPInstruction opc);
|
||||
void dr(const UDSPInstruction opc);
|
||||
void ir(const UDSPInstruction opc);
|
||||
void nr(const UDSPInstruction opc);
|
||||
void nop(const UDSPInstruction opc);
|
||||
|
||||
} // end namespace Ext
|
||||
} // end namespace DSPinterpeter
|
||||
|
||||
#endif
|
||||
386
Source/Core/Core/Src/DSP/DSPIntUtil.h
Normal file
386
Source/Core/Core/Src/DSP/DSPIntUtil.h
Normal file
@@ -0,0 +1,386 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_opcodes_helper.h
|
||||
project: GameCube DSP Tool (gcdsp)
|
||||
created: 2005.03.04
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#ifndef _DSP_INT_UTIL_H
|
||||
#define _DSP_INT_UTIL_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPMemoryMap.h"
|
||||
#include "DSPStacks.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// --- SR
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
inline void dsp_SR_set_flag(int flag)
|
||||
{
|
||||
g_dsp.r.sr |= flag;
|
||||
}
|
||||
|
||||
inline bool dsp_SR_is_flag_set(int flag)
|
||||
{
|
||||
return (g_dsp.r.sr & flag) != 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// --- AR increments, decrements
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
inline u16 dsp_increase_addr_reg(u16 reg, s16 _ix)
|
||||
{
|
||||
u32 ar = g_dsp.r.ar[reg];
|
||||
u32 wr = g_dsp.r.wr[reg];
|
||||
s32 ix = _ix;
|
||||
|
||||
u32 mx = (wr | 1) << 1;
|
||||
u32 nar = ar + ix;
|
||||
u32 dar = (nar ^ ar ^ ix) & mx;
|
||||
|
||||
if (ix >= 0)
|
||||
{
|
||||
if (dar > wr) //overflow
|
||||
nar -= wr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((((nar + wr + 1) ^ nar) & dar) <= wr) //underflow or below min for mask
|
||||
nar += wr + 1;
|
||||
}
|
||||
return nar;
|
||||
}
|
||||
|
||||
inline u16 dsp_decrease_addr_reg(u16 reg, s16 _ix)
|
||||
{
|
||||
u32 ar = g_dsp.r.ar[reg];
|
||||
u32 wr = g_dsp.r.wr[reg];
|
||||
s32 ix = _ix;
|
||||
|
||||
u32 mx = (wr | 1) << 1;
|
||||
u32 nar = ar - ix;
|
||||
u32 dar = (nar ^ ar ^ ~ix) & mx;
|
||||
|
||||
if ((u32)ix > 0xFFFF8000) //(ix < 0 && ix != -0x8000)
|
||||
{
|
||||
if (dar > wr) //overflow
|
||||
nar -= wr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((((nar + wr + 1) ^ nar) & dar) <= wr) //underflow or below min for mask
|
||||
nar += wr + 1;
|
||||
}
|
||||
return nar;
|
||||
}
|
||||
|
||||
inline u16 dsp_increment_addr_reg(u16 reg)
|
||||
{
|
||||
u32 ar = g_dsp.r.ar[reg];
|
||||
u32 wr = g_dsp.r.wr[reg];
|
||||
|
||||
u32 nar = ar + 1;
|
||||
|
||||
if ((nar ^ ar) > ((wr | 1) << 1))
|
||||
nar -= wr + 1;
|
||||
return nar;
|
||||
}
|
||||
|
||||
inline u16 dsp_decrement_addr_reg(u16 reg)
|
||||
{
|
||||
u32 ar = g_dsp.r.ar[reg];
|
||||
u32 wr = g_dsp.r.wr[reg];
|
||||
|
||||
u32 nar = ar + wr;
|
||||
|
||||
if (((nar ^ ar) & ((wr | 1) << 1)) > wr)
|
||||
nar -= wr + 1;
|
||||
return nar;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// --- reg
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
inline u16 dsp_op_read_reg(int reg)
|
||||
{
|
||||
switch (reg & 0x1f) {
|
||||
case DSP_REG_ST0:
|
||||
case DSP_REG_ST1:
|
||||
case DSP_REG_ST2:
|
||||
case DSP_REG_ST3:
|
||||
return dsp_reg_load_stack(reg - DSP_REG_ST0);
|
||||
case DSP_REG_AR0:
|
||||
case DSP_REG_AR1:
|
||||
case DSP_REG_AR2:
|
||||
case DSP_REG_AR3:
|
||||
return g_dsp.r.ar[reg - DSP_REG_AR0];
|
||||
case DSP_REG_IX0:
|
||||
case DSP_REG_IX1:
|
||||
case DSP_REG_IX2:
|
||||
case DSP_REG_IX3:
|
||||
return g_dsp.r.ix[reg - DSP_REG_IX0];
|
||||
case DSP_REG_WR0:
|
||||
case DSP_REG_WR1:
|
||||
case DSP_REG_WR2:
|
||||
case DSP_REG_WR3:
|
||||
return g_dsp.r.wr[reg - DSP_REG_WR0];
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
return g_dsp.r.ac[reg - DSP_REG_ACH0].h;
|
||||
case DSP_REG_CR: return g_dsp.r.cr;
|
||||
case DSP_REG_SR: return g_dsp.r.sr;
|
||||
case DSP_REG_PRODL: return g_dsp.r.prod.l;
|
||||
case DSP_REG_PRODM: return g_dsp.r.prod.m;
|
||||
case DSP_REG_PRODH: return g_dsp.r.prod.h;
|
||||
case DSP_REG_PRODM2: return g_dsp.r.prod.m2;
|
||||
case DSP_REG_AXL0:
|
||||
case DSP_REG_AXL1:
|
||||
return g_dsp.r.ax[reg - DSP_REG_AXL0].l;
|
||||
case DSP_REG_AXH0:
|
||||
case DSP_REG_AXH1:
|
||||
return g_dsp.r.ax[reg - DSP_REG_AXH0].h;
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
return g_dsp.r.ac[reg - DSP_REG_ACL0].l;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
return g_dsp.r.ac[reg - DSP_REG_ACM0].m;
|
||||
default:
|
||||
_assert_msg_(DSP_INT, 0, "cannot happen");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void dsp_op_write_reg(int reg, u16 val)
|
||||
{
|
||||
switch (reg & 0x1f) {
|
||||
// 8-bit sign extended registers. Should look at prod.h too...
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
// sign extend from the bottom 8 bits.
|
||||
g_dsp.r.ac[reg-DSP_REG_ACH0].h = (u16)(s16)(s8)(u8)val;
|
||||
break;
|
||||
|
||||
// Stack registers.
|
||||
case DSP_REG_ST0:
|
||||
case DSP_REG_ST1:
|
||||
case DSP_REG_ST2:
|
||||
case DSP_REG_ST3:
|
||||
dsp_reg_store_stack(reg - DSP_REG_ST0, val);
|
||||
break;
|
||||
|
||||
case DSP_REG_AR0:
|
||||
case DSP_REG_AR1:
|
||||
case DSP_REG_AR2:
|
||||
case DSP_REG_AR3:
|
||||
g_dsp.r.ar[reg - DSP_REG_AR0] = val;
|
||||
break;
|
||||
case DSP_REG_IX0:
|
||||
case DSP_REG_IX1:
|
||||
case DSP_REG_IX2:
|
||||
case DSP_REG_IX3:
|
||||
g_dsp.r.ix[reg - DSP_REG_IX0] = val;
|
||||
break;
|
||||
case DSP_REG_WR0:
|
||||
case DSP_REG_WR1:
|
||||
case DSP_REG_WR2:
|
||||
case DSP_REG_WR3:
|
||||
g_dsp.r.wr[reg - DSP_REG_WR0] = val;
|
||||
break;
|
||||
case DSP_REG_CR: g_dsp.r.cr = val; break;
|
||||
case DSP_REG_SR: g_dsp.r.sr = val; break;
|
||||
case DSP_REG_PRODL: g_dsp.r.prod.l = val; break;
|
||||
case DSP_REG_PRODM: g_dsp.r.prod.m = val; break;
|
||||
case DSP_REG_PRODH: g_dsp.r.prod.h = val; break;
|
||||
case DSP_REG_PRODM2: g_dsp.r.prod.m2 = val; break;
|
||||
case DSP_REG_AXL0:
|
||||
case DSP_REG_AXL1:
|
||||
g_dsp.r.ax[reg - DSP_REG_AXL0].l = val;
|
||||
break;
|
||||
case DSP_REG_AXH0:
|
||||
case DSP_REG_AXH1:
|
||||
g_dsp.r.ax[reg - DSP_REG_AXH0].h = val;
|
||||
break;
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
g_dsp.r.ac[reg - DSP_REG_ACL0].l = val;
|
||||
break;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
g_dsp.r.ac[reg - DSP_REG_ACM0].m = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void dsp_conditional_extend_accum(int reg)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
if (g_dsp.r.sr & SR_40_MODE_BIT)
|
||||
{
|
||||
// Sign extend into whole accum.
|
||||
u16 val = g_dsp.r.ac[reg-DSP_REG_ACM0].m;
|
||||
g_dsp.r.ac[reg - DSP_REG_ACM0].h = (val & 0x8000) ? 0xFFFF : 0x0000;
|
||||
g_dsp.r.ac[reg - DSP_REG_ACM0].l = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// --- prod
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
inline s64 dsp_get_long_prod()
|
||||
{
|
||||
#if PROFILE
|
||||
ProfilerAddDelta(g_dsp.err_pc, 1);
|
||||
#endif
|
||||
|
||||
s64 val = (s8)(u8)g_dsp.r.prod.h;
|
||||
val <<= 32;
|
||||
s64 low_prod = g_dsp.r.prod.m;
|
||||
low_prod += g_dsp.r.prod.m2;
|
||||
low_prod <<= 16;
|
||||
low_prod |= g_dsp.r.prod.l;
|
||||
val += low_prod;
|
||||
return val;
|
||||
}
|
||||
|
||||
inline s64 dsp_get_long_prod_round_prodl()
|
||||
{
|
||||
s64 prod = dsp_get_long_prod();
|
||||
|
||||
if (prod & 0x10000)
|
||||
prod = (prod + 0x8000) & ~0xffff;
|
||||
else
|
||||
prod = (prod + 0x7fff) & ~0xffff;
|
||||
|
||||
return prod;
|
||||
}
|
||||
|
||||
// For accurate emulation, this is wrong - but the real prod registers behave
|
||||
// in completely bizarre ways. Not needed to emulate them correctly for game ucodes.
|
||||
inline void dsp_set_long_prod(s64 val)
|
||||
{
|
||||
#if PROFILE
|
||||
ProfilerAddDelta(g_dsp.err_pc, 1);
|
||||
#endif
|
||||
|
||||
g_dsp.r.prod.l = (u16)val;
|
||||
val >>= 16;
|
||||
g_dsp.r.prod.m = (u16)val;
|
||||
val >>= 16;
|
||||
g_dsp.r.prod.h = /*(s16)(s8)*/(u8)val;//todo: check expansion
|
||||
g_dsp.r.prod.m2 = 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// --- ACC - main accumulators (40-bit)
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
inline s64 dsp_get_long_acc(int reg)
|
||||
{
|
||||
#if PROFILE
|
||||
ProfilerAddDelta(g_dsp.err_pc, 1);
|
||||
#endif
|
||||
|
||||
s64 high = (s64)(s8)g_dsp.r.ac[reg].h << 32;
|
||||
u32 mid_low = ((u32)g_dsp.r.ac[reg].m << 16) | g_dsp.r.ac[reg].l;
|
||||
return high | mid_low;
|
||||
}
|
||||
|
||||
inline void dsp_set_long_acc(int _reg, s64 val)
|
||||
{
|
||||
#if PROFILE
|
||||
ProfilerAddDelta(g_dsp.err_pc, 1);
|
||||
#endif
|
||||
|
||||
g_dsp.r.ac[_reg].l = (u16)val;
|
||||
val >>= 16;
|
||||
g_dsp.r.ac[_reg].m = (u16)val;
|
||||
val >>= 16;
|
||||
g_dsp.r.ac[_reg].h = (u16)(s16)(s8)(u8)val;
|
||||
}
|
||||
|
||||
inline s64 dsp_convert_long_acc(s64 val) // s64 -> s40
|
||||
{
|
||||
return ((s64)(s8)(val >> 32))<<32 | (u32)val;
|
||||
}
|
||||
|
||||
inline s64 dsp_round_long_acc(s64 val)
|
||||
{
|
||||
if (val & 0x10000)
|
||||
val = (val + 0x8000) & ~0xffff;
|
||||
else
|
||||
val = (val + 0x7fff) & ~0xffff;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
inline s16 dsp_get_acc_l(int _reg)
|
||||
{
|
||||
return g_dsp.r.ac[_reg].l;
|
||||
}
|
||||
|
||||
inline s16 dsp_get_acc_m(int _reg)
|
||||
{
|
||||
return g_dsp.r.ac[_reg].m;
|
||||
}
|
||||
|
||||
inline s16 dsp_get_acc_h(int _reg)
|
||||
{
|
||||
return g_dsp.r.ac[_reg].h;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// --- AX - extra accumulators (32-bit)
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
inline s32 dsp_get_long_acx(int _reg)
|
||||
{
|
||||
#if PROFILE
|
||||
ProfilerAddDelta(g_dsp.err_pc, 1);
|
||||
#endif
|
||||
|
||||
return ((u32)g_dsp.r.ax[_reg].h << 16) | g_dsp.r.ax[_reg].l;
|
||||
}
|
||||
|
||||
inline s16 dsp_get_ax_l(int _reg)
|
||||
{
|
||||
return (s16)g_dsp.r.ax[_reg].l;
|
||||
}
|
||||
|
||||
inline s16 dsp_get_ax_h(int _reg)
|
||||
{
|
||||
return (s16)g_dsp.r.ax[_reg].h;
|
||||
}
|
||||
|
||||
#endif
|
||||
261
Source/Core/Core/Src/DSP/DSPInterpreter.cpp
Normal file
261
Source/Core/Core/Src/DSP/DSPInterpreter.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_interpreter.cpp
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#include "DSPTables.h"
|
||||
#include "DSPHost.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPAnalyzer.h"
|
||||
|
||||
#include "DSPHWInterface.h"
|
||||
#include "DSPIntUtil.h"
|
||||
|
||||
namespace DSPInterpreter {
|
||||
|
||||
volatile u32 gdsp_running;
|
||||
|
||||
// NOTE: These have nothing to do with g_dsp.r[DSP_REG_CR].
|
||||
|
||||
// Hm, should instructions that change CR use this? Probably not (but they
|
||||
// should call UpdateCachedCR())
|
||||
void WriteCR(u16 val)
|
||||
{
|
||||
// reset
|
||||
if (val & 1)
|
||||
{
|
||||
DSPCore_Reset();
|
||||
val &= ~1;
|
||||
}
|
||||
// init - can reset and init be done at the same time?
|
||||
else if (val == 4)
|
||||
{
|
||||
// this looks like a hack! OSInitAudioSystem ucode
|
||||
// should send this mail - not dsp core itself
|
||||
gdsp_mbox_write_h(GDSP_MBOX_DSP, 0x8054);
|
||||
gdsp_mbox_write_l(GDSP_MBOX_DSP, 0x4348);
|
||||
val |= 0x800;
|
||||
}
|
||||
|
||||
// update cr
|
||||
g_dsp.cr = val;
|
||||
}
|
||||
|
||||
// Hm, should instructions that read CR use this? (Probably not).
|
||||
u16 ReadCR()
|
||||
{
|
||||
if (g_dsp.pc & 0x8000)
|
||||
{
|
||||
g_dsp.cr |= 0x800;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_dsp.cr &= ~0x800;
|
||||
}
|
||||
|
||||
return g_dsp.cr;
|
||||
}
|
||||
|
||||
void Step()
|
||||
{
|
||||
DSPCore_CheckExceptions();
|
||||
|
||||
g_dsp.step_counter++;
|
||||
|
||||
#if PROFILE
|
||||
g_dsp.err_pc = g_dsp.pc;
|
||||
|
||||
ProfilerAddDelta(g_dsp.err_pc, 1);
|
||||
if (g_dsp.step_counter == 1)
|
||||
{
|
||||
ProfilerInit();
|
||||
}
|
||||
|
||||
if ((g_dsp.step_counter & 0xFFFFF) == 0)
|
||||
{
|
||||
ProfilerDump(g_dsp.step_counter);
|
||||
}
|
||||
#endif
|
||||
|
||||
u16 opc = dsp_fetch_code();
|
||||
ExecuteInstruction(UDSPInstruction(opc));
|
||||
|
||||
if (DSPAnalyzer::code_flags[g_dsp.pc - 1] & DSPAnalyzer::CODE_LOOP_END)
|
||||
HandleLoop();
|
||||
}
|
||||
|
||||
// Used by thread mode.
|
||||
//void Run()
|
||||
//{
|
||||
// int checkInterrupt = 0;
|
||||
// gdsp_running = true;
|
||||
// while (!(g_dsp.cr & CR_HALT) && gdsp_running)
|
||||
// {
|
||||
// if (jit)
|
||||
// jit->RunForCycles(1);
|
||||
// else {
|
||||
// // Automatically let the other threads work if we're idle skipping
|
||||
// if(DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
|
||||
// Common::YieldCPU();
|
||||
//
|
||||
// Step();
|
||||
//
|
||||
// // Turns out the less you check for external interrupts, the more
|
||||
// // sound you hear, and it becomes slower
|
||||
// checkInterrupt++;
|
||||
// if(checkInterrupt == 500) { // <-- Arbitrary number. TODO: tweak
|
||||
// DSPCore_CheckExternalInterrupt();
|
||||
// checkInterrupt = 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// gdsp_running = false;
|
||||
//}
|
||||
|
||||
// This one has basic idle skipping, and checks breakpoints.
|
||||
int RunCyclesDebug(int cycles)
|
||||
{
|
||||
// First, let's run a few cycles with no idle skipping so that things can progress a bit.
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (g_dsp.cr & CR_HALT)
|
||||
return 0;
|
||||
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
|
||||
{
|
||||
DSPCore_SetState(DSPCORE_STEPPING);
|
||||
return cycles;
|
||||
}
|
||||
Step();
|
||||
cycles--;
|
||||
if (cycles < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// In thread mode, process external interrupts
|
||||
if (g_dsp.external_interrupt_waiting)
|
||||
{
|
||||
DSPCore_CheckExternalInterrupt();
|
||||
DSPCore_CheckExceptions();
|
||||
DSPCore_SetExternalInterrupt(false);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Next, let's run a few cycles with idle skipping, so that we can skip
|
||||
// idle loops.
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (g_dsp.cr & CR_HALT)
|
||||
return 0;
|
||||
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
|
||||
{
|
||||
DSPCore_SetState(DSPCORE_STEPPING);
|
||||
return cycles;
|
||||
}
|
||||
// Idle skipping.
|
||||
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
|
||||
return 0;
|
||||
Step();
|
||||
cycles--;
|
||||
if (cycles < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Now, lets run some more without idle skipping.
|
||||
for (int i = 0; i < 200; i++)
|
||||
{
|
||||
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
|
||||
{
|
||||
DSPCore_SetState(DSPCORE_STEPPING);
|
||||
return cycles;
|
||||
}
|
||||
Step();
|
||||
cycles--;
|
||||
if (cycles < 0)
|
||||
return 0;
|
||||
|
||||
// We don't bother directly supporting pause - if the main emu pauses,
|
||||
// it just won't call this function anymore.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used by non-thread mode. Meant to be efficient.
|
||||
int RunCycles(int cycles)
|
||||
{
|
||||
// First, let's run a few cycles with no idle skipping so that things can
|
||||
// progress a bit.
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (g_dsp.cr & CR_HALT)
|
||||
return 0;
|
||||
Step();
|
||||
cycles--;
|
||||
if (cycles < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// In thread mode, process external interrupts
|
||||
if (g_dsp.external_interrupt_waiting)
|
||||
{
|
||||
DSPCore_CheckExternalInterrupt();
|
||||
DSPCore_CheckExceptions();
|
||||
DSPCore_SetExternalInterrupt(false);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Next, let's run a few cycles with idle skipping, so that we can skip
|
||||
// idle loops.
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (g_dsp.cr & CR_HALT)
|
||||
return 0;
|
||||
// Idle skipping.
|
||||
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
|
||||
return 0;
|
||||
Step();
|
||||
cycles--;
|
||||
if (cycles < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Now, lets run some more without idle skipping.
|
||||
for (int i = 0; i < 200; i++)
|
||||
{
|
||||
Step();
|
||||
cycles--;
|
||||
if (cycles < 0)
|
||||
return 0;
|
||||
// We don't bother directly supporting pause - if the main emu pauses,
|
||||
// it just won't call this function anymore.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
gdsp_running = false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
170
Source/Core/Core/Src/DSP/DSPInterpreter.h
Normal file
170
Source/Core/Core/Src/DSP/DSPInterpreter.h
Normal file
@@ -0,0 +1,170 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPINTERPRETER_H
|
||||
#define _DSPINTERPRETER_H
|
||||
|
||||
#include "DSPTables.h"
|
||||
|
||||
#define DSP_REG_MASK 0x1f
|
||||
|
||||
namespace DSPInterpreter {
|
||||
|
||||
void Step();
|
||||
void Run();
|
||||
|
||||
// See: DspIntBranch.cpp
|
||||
void HandleLoop();
|
||||
|
||||
// If these simply return the same number of cycles as was passed into them,
|
||||
// chances are that the DSP is halted.
|
||||
// The difference between them is that the debug one obeys breakpoints.
|
||||
int RunCycles(int cycles);
|
||||
int RunCyclesDebug(int cycles);
|
||||
|
||||
void Stop();
|
||||
|
||||
void WriteCR(u16 val);
|
||||
u16 ReadCR();
|
||||
|
||||
|
||||
typedef void (*DSPInterpreterFunc)(const UDSPInstruction opc);
|
||||
|
||||
// All the opcode functions.
|
||||
void unknown(const UDSPInstruction opc);
|
||||
void call(const UDSPInstruction opc);
|
||||
void callr(const UDSPInstruction opc);
|
||||
void ifcc(const UDSPInstruction opc);
|
||||
void jcc(const UDSPInstruction opc);
|
||||
void jmprcc(const UDSPInstruction opc);
|
||||
void ret(const UDSPInstruction opc);
|
||||
void halt(const UDSPInstruction opc);
|
||||
void loop(const UDSPInstruction opc);
|
||||
void loopi(const UDSPInstruction opc);
|
||||
void bloop(const UDSPInstruction opc);
|
||||
void bloopi(const UDSPInstruction opc);
|
||||
void mrr(const UDSPInstruction opc);
|
||||
void lrr(const UDSPInstruction opc);
|
||||
void lrrd(const UDSPInstruction opc);
|
||||
void lrri(const UDSPInstruction opc);
|
||||
void lrrn(const UDSPInstruction opc);
|
||||
void srr(const UDSPInstruction opc);
|
||||
void srrd(const UDSPInstruction opc);
|
||||
void srri(const UDSPInstruction opc);
|
||||
void srrn(const UDSPInstruction opc);
|
||||
void lri(const UDSPInstruction opc);
|
||||
void lris(const UDSPInstruction opc);
|
||||
void lr(const UDSPInstruction opc);
|
||||
void sr(const UDSPInstruction opc);
|
||||
void si(const UDSPInstruction opc);
|
||||
void tstaxh(const UDSPInstruction opc);
|
||||
void clr(const UDSPInstruction opc);
|
||||
void clrl(const UDSPInstruction opc);
|
||||
void clrp(const UDSPInstruction opc);
|
||||
void mulc(const UDSPInstruction opc);
|
||||
void cmpar(const UDSPInstruction opc);
|
||||
void cmp(const UDSPInstruction opc);
|
||||
void tst(const UDSPInstruction opc);
|
||||
void addaxl(const UDSPInstruction opc);
|
||||
void addarn(const UDSPInstruction opc);
|
||||
void mulcac(const UDSPInstruction opc);
|
||||
void movr(const UDSPInstruction opc);
|
||||
void movax(const UDSPInstruction opc);
|
||||
void xorr(const UDSPInstruction opc);
|
||||
void andr(const UDSPInstruction opc);
|
||||
void orr(const UDSPInstruction opc);
|
||||
void andc(const UDSPInstruction opc);
|
||||
void orc(const UDSPInstruction opc);
|
||||
void xorc(const UDSPInstruction opc);
|
||||
void notc(const UDSPInstruction opc);
|
||||
void lsrnrx(const UDSPInstruction opc);
|
||||
void asrnrx(const UDSPInstruction opc);
|
||||
void lsrnr(const UDSPInstruction opc);
|
||||
void asrnr(const UDSPInstruction opc);
|
||||
void add(const UDSPInstruction opc);
|
||||
void addp(const UDSPInstruction opc);
|
||||
void cmpis(const UDSPInstruction opc);
|
||||
void addpaxz(const UDSPInstruction opc);
|
||||
void movpz(const UDSPInstruction opc);
|
||||
void decm(const UDSPInstruction opc);
|
||||
void dec(const UDSPInstruction opc);
|
||||
void inc(const UDSPInstruction opc);
|
||||
void incm(const UDSPInstruction opc);
|
||||
void neg(const UDSPInstruction opc);
|
||||
void addax(const UDSPInstruction opc);
|
||||
void addr(const UDSPInstruction opc);
|
||||
void subr(const UDSPInstruction opc);
|
||||
void subp(const UDSPInstruction opc);
|
||||
void subax(const UDSPInstruction opc);
|
||||
void addis(const UDSPInstruction opc);
|
||||
void addi(const UDSPInstruction opc);
|
||||
void lsl16(const UDSPInstruction opc);
|
||||
void madd(const UDSPInstruction opc);
|
||||
void msub(const UDSPInstruction opc);
|
||||
void lsr16(const UDSPInstruction opc);
|
||||
void asr16(const UDSPInstruction opc);
|
||||
void lsl(const UDSPInstruction opc);
|
||||
void lsr(const UDSPInstruction opc);
|
||||
void asl(const UDSPInstruction opc);
|
||||
void asr(const UDSPInstruction opc);
|
||||
void lsrn(const UDSPInstruction opc);
|
||||
void asrn(const UDSPInstruction opc);
|
||||
void dar(const UDSPInstruction opc);
|
||||
void iar(const UDSPInstruction opc);
|
||||
void subarn(const UDSPInstruction opc);
|
||||
void sbclr(const UDSPInstruction opc);
|
||||
void sbset(const UDSPInstruction opc);
|
||||
void mov(const UDSPInstruction opc);
|
||||
void movp(const UDSPInstruction opc);
|
||||
void mul(const UDSPInstruction opc);
|
||||
void mulac(const UDSPInstruction opc);
|
||||
void mulmv(const UDSPInstruction opc);
|
||||
void mulmvz(const UDSPInstruction opc);
|
||||
void mulx(const UDSPInstruction opc);
|
||||
void mulxac(const UDSPInstruction opc);
|
||||
void mulxmv(const UDSPInstruction opc);
|
||||
void mulxmvz(const UDSPInstruction opc);
|
||||
void mulcmvz(const UDSPInstruction opc);
|
||||
void mulcmv(const UDSPInstruction opc);
|
||||
void movnp(const UDSPInstruction opc);
|
||||
void sub(const UDSPInstruction opc);
|
||||
void maddx(const UDSPInstruction opc);
|
||||
void msubx(const UDSPInstruction opc);
|
||||
void maddc(const UDSPInstruction opc);
|
||||
void msubc(const UDSPInstruction opc);
|
||||
void srs(const UDSPInstruction opc);
|
||||
void lrs(const UDSPInstruction opc);
|
||||
void nx(const UDSPInstruction opc);
|
||||
void cmpi(const UDSPInstruction opc);
|
||||
void rti(const UDSPInstruction opc);
|
||||
void ilrr(const UDSPInstruction opc);
|
||||
void ilrrd(const UDSPInstruction opc);
|
||||
void ilrri(const UDSPInstruction opc);
|
||||
void ilrrn(const UDSPInstruction opc);
|
||||
void andcf(const UDSPInstruction opc);
|
||||
void andf(const UDSPInstruction opc);
|
||||
void xori(const UDSPInstruction opc);
|
||||
void andi(const UDSPInstruction opc);
|
||||
void ori(const UDSPInstruction opc);
|
||||
void srbith(const UDSPInstruction opc);
|
||||
void mulaxh(const UDSPInstruction opc);
|
||||
void tstprod(const UDSPInstruction opc);
|
||||
void abs(const UDSPInstruction opc);
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // _DSPINTERPRETER_H
|
||||
83
Source/Core/Core/Src/DSP/DSPMemoryMap.cpp
Normal file
83
Source/Core/Core/Src/DSP/DSPMemoryMap.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_memory.cpp
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPMemoryMap.h"
|
||||
#include "DSPHWInterface.h"
|
||||
#include "DSPCore.h"
|
||||
|
||||
u16 dsp_imem_read(u16 addr)
|
||||
{
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0: // 0xxx IRAM
|
||||
return g_dsp.iram[addr & DSP_IRAM_MASK];
|
||||
|
||||
case 8: // 8xxx IROM - contains code to receive code for IRAM, and a bunch of mixing loops.
|
||||
return g_dsp.irom[addr & DSP_IROM_MASK];
|
||||
|
||||
default: // Unmapped/non-existing memory
|
||||
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Executing from invalid (%04x) memory", g_dsp.pc, addr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u16 dsp_dmem_read(u16 addr)
|
||||
{
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0x0: // 0xxx DRAM
|
||||
return g_dsp.dram[addr & DSP_DRAM_MASK];
|
||||
|
||||
case 0x1: // 1xxx COEF
|
||||
DEBUG_LOG(DSPLLE, "%04x : Coef Read @ %04x", g_dsp.pc, addr);
|
||||
return g_dsp.coef[addr & DSP_COEF_MASK];
|
||||
|
||||
case 0xf: // Fxxx HW regs
|
||||
return gdsp_ifx_read(addr);
|
||||
|
||||
default: // Unmapped/non-existing memory
|
||||
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Read from UNKNOWN (%04x) memory", g_dsp.pc, addr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dsp_dmem_write(u16 addr, u16 val)
|
||||
{
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0x0: // 0xxx DRAM
|
||||
g_dsp.dram[addr & DSP_DRAM_MASK] = val;
|
||||
break;
|
||||
|
||||
case 0xf: // Fxxx HW regs
|
||||
gdsp_ifx_write(addr, val);
|
||||
break;
|
||||
|
||||
default: // Unmapped/non-existing memory
|
||||
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Write to UNKNOWN (%04x) memory", g_dsp.pc, addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
55
Source/Core/Core/Src/DSP/DSPMemoryMap.h
Normal file
55
Source/Core/Core/Src/DSP/DSPMemoryMap.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_memory.h
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#ifndef _GDSP_MEMORY_H
|
||||
#define _GDSP_MEMORY_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPCore.h"
|
||||
|
||||
u16 dsp_imem_read(u16 addr);
|
||||
void dsp_dmem_write(u16 addr, u16 val);
|
||||
u16 dsp_dmem_read(u16 addr);
|
||||
|
||||
inline u16 dsp_fetch_code()
|
||||
{
|
||||
u16 opc = dsp_imem_read(g_dsp.pc);
|
||||
|
||||
g_dsp.pc++;
|
||||
return opc;
|
||||
}
|
||||
|
||||
inline u16 dsp_peek_code()
|
||||
{
|
||||
return dsp_imem_read(g_dsp.pc);
|
||||
}
|
||||
|
||||
inline void dsp_skip_inst()
|
||||
{
|
||||
g_dsp.pc += opTable[dsp_peek_code()]->size;
|
||||
}
|
||||
|
||||
#endif
|
||||
58
Source/Core/Core/Src/DSP/DSPStacks.cpp
Normal file
58
Source/Core/Core/Src/DSP/DSPStacks.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_registers.cpp
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "DSPCore.h"
|
||||
#include "DSPStacks.h"
|
||||
|
||||
// Stacks. The stacks are outside the DSP RAM, in dedicated hardware.
|
||||
|
||||
void dsp_reg_stack_push(int stack_reg)
|
||||
{
|
||||
g_dsp.reg_stack_ptr[stack_reg]++;
|
||||
g_dsp.reg_stack_ptr[stack_reg] &= DSP_STACK_MASK;
|
||||
g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]] = g_dsp.r.st[stack_reg];
|
||||
}
|
||||
|
||||
void dsp_reg_stack_pop(int stack_reg)
|
||||
{
|
||||
g_dsp.r.st[stack_reg] = g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]];
|
||||
g_dsp.reg_stack_ptr[stack_reg]--;
|
||||
g_dsp.reg_stack_ptr[stack_reg] &= DSP_STACK_MASK;
|
||||
}
|
||||
|
||||
void dsp_reg_store_stack(int stack_reg, u16 val)
|
||||
{
|
||||
dsp_reg_stack_push(stack_reg);
|
||||
g_dsp.r.st[stack_reg] = val;
|
||||
}
|
||||
|
||||
u16 dsp_reg_load_stack(int stack_reg)
|
||||
{
|
||||
u16 val = g_dsp.r.st[stack_reg];
|
||||
dsp_reg_stack_pop(stack_reg);
|
||||
return val;
|
||||
}
|
||||
34
Source/Core/Core/Src/DSP/DSPStacks.h
Normal file
34
Source/Core/Core/Src/DSP/DSPStacks.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: gdsp_registers.h
|
||||
project: GCemu
|
||||
created: 2004-6-18
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie & Tratax
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#ifndef _DSP_STACKS_H
|
||||
#define _DSP_STACKS_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
void dsp_reg_store_stack(int stack_reg, u16 val);
|
||||
u16 dsp_reg_load_stack(int stack_reg);
|
||||
|
||||
#endif
|
||||
568
Source/Core/Core/Src/DSP/DSPTables.cpp
Normal file
568
Source/Core/Core/Src/DSP/DSPTables.cpp
Normal file
@@ -0,0 +1,568 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie (c) 2005 (duddie@walla.com)
|
||||
|
||||
#include "Common.h"
|
||||
#include "DSPTables.h"
|
||||
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPIntExtOps.h"
|
||||
#include "DSPEmitter.h"
|
||||
|
||||
void nop(const UDSPInstruction opc)
|
||||
{
|
||||
// The real nop is 0. Anything else is bad.
|
||||
if (opc)
|
||||
DSPInterpreter::unknown(opc);
|
||||
}
|
||||
|
||||
const DSPOPCTemplate opcodes[] =
|
||||
{
|
||||
{"NOP", 0x0000, 0xfffc, nop, &DSPEmitter::nop, 1, 0, {}, false, false, false, false, false},
|
||||
|
||||
{"DAR", 0x0004, 0xfffc, DSPInterpreter::dar, &DSPEmitter::dar, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
|
||||
{"IAR", 0x0008, 0xfffc, DSPInterpreter::iar, &DSPEmitter::iar, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
|
||||
{"SUBARN", 0x000c, 0xfffc, DSPInterpreter::subarn, &DSPEmitter::subarn, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
|
||||
{"ADDARN", 0x0010, 0xfff0, DSPInterpreter::addarn, &DSPEmitter::addarn, 1, 2, {{P_REG, 1, 0, 0, 0x0003}, {P_REG04, 1, 0, 2, 0x000c}}, false, false, false, false, false},
|
||||
|
||||
{"HALT", 0x0021, 0xffff, DSPInterpreter::halt, &DSPEmitter::halt, 1, 0, {}, false, true, true, false, false},
|
||||
|
||||
{"RETGE", 0x02d0, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETL", 0x02d1, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETG", 0x02d2, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETLE", 0x02d3, 0xffff, DSPInterpreter::ret, NULL/*&DSPEmitter::ret*/, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETNZ", 0x02d4, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETZ", 0x02d5, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETNC", 0x02d6, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETC", 0x02d7, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETx8", 0x02d8, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETx9", 0x02d9, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETxA", 0x02da, 0xffff, DSPInterpreter::ret, NULL/*&DSPEmitter::ret*/, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETxB", 0x02db, 0xffff, DSPInterpreter::ret, NULL/*&DSPEmitter::ret*/, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETLNZ", 0x02dc, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETLZ", 0x02dd, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RETO", 0x02de, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
|
||||
{"RET", 0x02df, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, true, false, false},
|
||||
|
||||
{"RTI", 0x02ff, 0xffff, DSPInterpreter::rti, &DSPEmitter::rti, 1, 0, {}, false, true, true, false, false},
|
||||
|
||||
{"CALLGE", 0x02b0, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLL", 0x02b1, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLG", 0x02b2, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLLE", 0x02b3, 0xffff, DSPInterpreter::call, NULL/*&DSPEmitter::call*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLNZ", 0x02b4, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLZ", 0x02b5, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLNC", 0x02b6, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLC", 0x02b7, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLx8", 0x02b8, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLx9", 0x02b9, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLxA", 0x02ba, 0xffff, DSPInterpreter::call, NULL/*&DSPEmitter::call*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLxB", 0x02bb, 0xffff, DSPInterpreter::call, NULL/*&DSPEmitter::call*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLLNZ", 0x02bc, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLLZ", 0x02bd, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALLO", 0x02be, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"CALL", 0x02bf, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, true, true, false},
|
||||
|
||||
{"IFGE", 0x0270, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFL", 0x0271, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFG", 0x0272, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFLE", 0x0273, 0xffff, DSPInterpreter::ifcc, NULL/*&DSPEmitter::ifcc*/, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFNZ", 0x0274, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFZ", 0x0275, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFNC", 0x0276, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFC", 0x0277, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFx8", 0x0278, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFx9", 0x0279, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFxA", 0x027a, 0xffff, DSPInterpreter::ifcc, NULL/*&DSPEmitter::ifcc*/, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFxB", 0x027b, 0xffff, DSPInterpreter::ifcc, NULL/*&DSPEmitter::ifcc*/, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFLNZ", 0x027c, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFLZ", 0x027d, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IFO", 0x027e, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
|
||||
{"IF", 0x027f, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, true, true, false},
|
||||
|
||||
{"JGE", 0x0290, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JL", 0x0291, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JG", 0x0292, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JLE", 0x0293, 0xffff, DSPInterpreter::jcc, NULL/*&DSPEmitter::jcc*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JNZ", 0x0294, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JZ", 0x0295, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JNC", 0x0296, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JC", 0x0297, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JMPx8", 0x0298, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JMPx9", 0x0299, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JMPxA", 0x029a, 0xffff, DSPInterpreter::jcc, NULL/*&DSPEmitter::jcc*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JMPxB", 0x029b, 0xffff, DSPInterpreter::jcc, NULL/*&DSPEmitter::jcc*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JLNZ", 0x029c, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JLZ", 0x029d, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JO", 0x029e, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"JMP", 0x029f, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, true, true, false},
|
||||
|
||||
{"JRGE", 0x1700, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRL", 0x1701, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRG", 0x1702, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRLE", 0x1703, 0xff1f, DSPInterpreter::jmprcc, NULL/*&DSPEmitter::jmprcc*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRNZ", 0x1704, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRZ", 0x1705, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRNC", 0x1706, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRC", 0x1707, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JMPRx8", 0x1708, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JMPRx9", 0x1709, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JMPRxA", 0x170a, 0xff1f, DSPInterpreter::jmprcc, NULL/*&DSPEmitter::jmprcc*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JMPRxB", 0x170b, 0xff1f, DSPInterpreter::jmprcc, NULL/*&DSPEmitter::jmprcc*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRLNZ", 0x170c, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRLZ", 0x170d, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JRO", 0x170e, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
|
||||
{"JMPR", 0x170f, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, true, false, false},
|
||||
|
||||
{"CALLRGE", 0x1710, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRL", 0x1711, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRG", 0x1712, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRLE", 0x1713, 0xff1f, DSPInterpreter::callr, NULL/*&DSPEmitter::callr*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRNZ", 0x1714, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRZ", 0x1715, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRNC", 0x1716, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRC", 0x1717, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRx8", 0x1718, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRx9", 0x1719, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRxA", 0x171a, 0xff1f, DSPInterpreter::callr, NULL/*&DSPEmitter::callr*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRxB", 0x171b, 0xff1f, DSPInterpreter::callr, NULL/*&DSPEmitter::callr*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRLNZ",0x171c, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRLZ", 0x171d, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLRO", 0x171e, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
|
||||
{"CALLR", 0x171f, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, true, true, false},
|
||||
|
||||
{"SBCLR", 0x1200, 0xff00, DSPInterpreter::sbclr, &DSPEmitter::sbclr, 1, 1, {{P_IMM, 1, 0, 0, 0x0007}}, false, false, false, false, false},
|
||||
{"SBSET", 0x1300, 0xff00, DSPInterpreter::sbset, &DSPEmitter::sbset, 1, 1, {{P_IMM, 1, 0, 0, 0x0007}}, false, false, false, false, false},
|
||||
|
||||
{"LSL", 0x1400, 0xfec0, DSPInterpreter::lsl, &DSPEmitter::lsl, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x003f}}, false, false, false, false, true},
|
||||
{"LSR", 0x1440, 0xfec0, DSPInterpreter::lsr, &DSPEmitter::lsr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x003f}}, false, false, false, false, true},
|
||||
{"ASL", 0x1480, 0xfec0, DSPInterpreter::asl, &DSPEmitter::asl, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x003f}}, false, false, false, false, true},
|
||||
{"ASR", 0x14c0, 0xfec0, DSPInterpreter::asr, &DSPEmitter::asr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x003f}}, false, false, false, false, true},
|
||||
|
||||
{"LSRN", 0x02ca, 0xffff, DSPInterpreter::lsrn, &DSPEmitter::lsrn, 1, 0, {}, false, false, false, false, true}, // discovered by ector!
|
||||
{"ASRN", 0x02cb, 0xffff, DSPInterpreter::asrn, &DSPEmitter::asrn, 1, 0, {}, false, false, false, false, true}, // discovered by ector!
|
||||
|
||||
{"LRI", 0x0080, 0xffe0, DSPInterpreter::lri, &DSPEmitter::lri, 2, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, false},
|
||||
{"LR", 0x00c0, 0xffe0, DSPInterpreter::lr, &DSPEmitter::lr, 2, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_MEM, 2, 1, 0, 0xffff}}, false, false, false, true, false},
|
||||
{"SR", 0x00e0, 0xffe0, DSPInterpreter::sr, &DSPEmitter::sr, 2, 2, {{P_MEM, 2, 1, 0, 0xffff}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, true, false},
|
||||
|
||||
{"MRR", 0x1c00, 0xfc00, DSPInterpreter::mrr, &DSPEmitter::mrr, 1, 2, {{P_REG, 1, 0, 5, 0x03e0}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
|
||||
|
||||
{"SI", 0x1600, 0xff00, DSPInterpreter::si, &DSPEmitter::si, 2, 2, {{P_MEM, 1, 0, 0, 0x00ff}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, false},
|
||||
|
||||
{"ADDIS", 0x0400, 0xfe00, DSPInterpreter::addis, &DSPEmitter::addis, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x00ff}}, false, false, false, false, true},
|
||||
{"CMPIS", 0x0600, 0xfe00, DSPInterpreter::cmpis, &DSPEmitter::cmpis, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x00ff}}, false, false, false, false, true},
|
||||
{"LRIS", 0x0800, 0xf800, DSPInterpreter::lris, &DSPEmitter::lris, 1, 2, {{P_REG18, 1, 0, 8, 0x0700}, {P_IMM, 1, 0, 0, 0x00ff}}, false, false, false, false, true},
|
||||
|
||||
{"ADDI", 0x0200, 0xfeff, DSPInterpreter::addi, &DSPEmitter::addi, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
|
||||
{"XORI", 0x0220, 0xfeff, DSPInterpreter::xori, &DSPEmitter::xori, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
|
||||
{"ANDI", 0x0240, 0xfeff, DSPInterpreter::andi, &DSPEmitter::andi, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
|
||||
{"ORI", 0x0260, 0xfeff, DSPInterpreter::ori, &DSPEmitter::ori, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
|
||||
{"CMPI", 0x0280, 0xfeff, DSPInterpreter::cmpi, &DSPEmitter::cmpi, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
|
||||
|
||||
{"ANDF", 0x02a0, 0xfeff, DSPInterpreter::andf, &DSPEmitter::andf, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
|
||||
{"ANDCF", 0x02c0, 0xfeff, DSPInterpreter::andcf, &DSPEmitter::andcf, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
|
||||
|
||||
{"ILRR", 0x0210, 0xfefc, DSPInterpreter::ilrr, &DSPEmitter::ilrr, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
|
||||
{"ILRRD", 0x0214, 0xfefc, DSPInterpreter::ilrrd, &DSPEmitter::ilrrd, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
|
||||
{"ILRRI", 0x0218, 0xfefc, DSPInterpreter::ilrri, &DSPEmitter::ilrri, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
|
||||
{"ILRRN", 0x021c, 0xfefc, DSPInterpreter::ilrrn, &DSPEmitter::ilrrn, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
|
||||
|
||||
// LOOPS
|
||||
{"LOOP", 0x0040, 0xffe0, DSPInterpreter::loop, &DSPEmitter::loop, 1, 1, {{P_REG, 1, 0, 0, 0x001f}}, false, true, false, true, false},
|
||||
{"BLOOP", 0x0060, 0xffe0, DSPInterpreter::bloop, &DSPEmitter::bloop, 2, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
{"LOOPI", 0x1000, 0xff00, DSPInterpreter::loopi, &DSPEmitter::loopi, 1, 1, {{P_IMM, 1, 0, 0, 0x00ff}}, false, true, false, true, false},
|
||||
{"BLOOPI", 0x1100, 0xff00, DSPInterpreter::bloopi, &DSPEmitter::bloopi, 2, 2, {{P_IMM, 1, 0, 0, 0x00ff}, {P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
|
||||
|
||||
// load and store value pointed by indexing reg and increment; LRR/SRR variants
|
||||
{"LRR", 0x1800, 0xff80, DSPInterpreter::lrr, &DSPEmitter::lrr, 1, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_PRG, 1, 0, 5, 0x0060}}, false, false, false, false, false},
|
||||
{"LRRD", 0x1880, 0xff80, DSPInterpreter::lrrd, &DSPEmitter::lrrd, 1, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_PRG, 1, 0, 5, 0x0060}}, false, false, false, false, false},
|
||||
{"LRRI", 0x1900, 0xff80, DSPInterpreter::lrri, &DSPEmitter::lrri, 1, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_PRG, 1, 0, 5, 0x0060}}, false, false, false, false, false},
|
||||
{"LRRN", 0x1980, 0xff80, DSPInterpreter::lrrn, &DSPEmitter::lrrn, 1, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_PRG, 1, 0, 5, 0x0060}}, false, false, false, false, false},
|
||||
|
||||
{"SRR", 0x1a00, 0xff80, DSPInterpreter::srr, &DSPEmitter::srr, 1, 2, {{P_PRG, 1, 0, 5, 0x0060}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
|
||||
{"SRRD", 0x1a80, 0xff80, DSPInterpreter::srrd, &DSPEmitter::srrd, 1, 2, {{P_PRG, 1, 0, 5, 0x0060}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
|
||||
{"SRRI", 0x1b00, 0xff80, DSPInterpreter::srri, &DSPEmitter::srri, 1, 2, {{P_PRG, 1, 0, 5, 0x0060}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
|
||||
{"SRRN", 0x1b80, 0xff80, DSPInterpreter::srrn, &DSPEmitter::srrn, 1, 2, {{P_PRG, 1, 0, 5, 0x0060}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
|
||||
|
||||
//2
|
||||
{"LRS", 0x2000, 0xf800, DSPInterpreter::lrs, &DSPEmitter::lrs, 1, 2, {{P_REG18, 1, 0, 8, 0x0700}, {P_MEM, 1, 0, 0, 0x00ff}}, false, false, false, false, false},
|
||||
{"SRS", 0x2800, 0xf800, DSPInterpreter::srs, &DSPEmitter::srs, 1, 2, {{P_MEM, 1, 0, 0, 0x00ff}, {P_REG18, 1, 0, 8, 0x0700}}, false, false, false, false, false},
|
||||
|
||||
// opcodes that can be extended
|
||||
|
||||
//3 - main opcode defined by 9 bits, extension defined by last 7 bits!!
|
||||
{"XORR", 0x3000, 0xfc80, DSPInterpreter::xorr, &DSPEmitter::xorr, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"ANDR", 0x3400, 0xfc80, DSPInterpreter::andr, &DSPEmitter::andr, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"ORR", 0x3800, 0xfc80, DSPInterpreter::orr, &DSPEmitter::orr, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"ANDC", 0x3c00, 0xfe80, DSPInterpreter::andc, &DSPEmitter::andc, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"ORC", 0x3e00, 0xfe80, DSPInterpreter::orc, &DSPEmitter::orc, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"XORC", 0x3080, 0xfe80, DSPInterpreter::xorc, &DSPEmitter::xorc, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"NOT", 0x3280, 0xfe80, DSPInterpreter::notc, &DSPEmitter::notc, 1, 1, {{P_ACCM, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"LSRNRX", 0x3480, 0xfc80, DSPInterpreter::lsrnrx, &DSPEmitter::lsrnrx, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"ASRNRX", 0x3880, 0xfc80, DSPInterpreter::asrnrx, &DSPEmitter::asrnrx, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"LSRNR", 0x3c80, 0xfe80, DSPInterpreter::lsrnr, &DSPEmitter::lsrnr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"ASRNR", 0x3e80, 0xfe80, DSPInterpreter::asrnr, &DSPEmitter::asrnr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
|
||||
//4
|
||||
{"ADDR", 0x4000, 0xf800, DSPInterpreter::addr, &DSPEmitter::addr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG18, 1, 0, 9, 0x0600}}, true, false, false, false, true},
|
||||
{"ADDAX", 0x4800, 0xfc00, DSPInterpreter::addax, &DSPEmitter::addax, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_AX, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"ADD", 0x4c00, 0xfe00, DSPInterpreter::add, &DSPEmitter::add, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACC_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"ADDP", 0x4e00, 0xfe00, DSPInterpreter::addp, &DSPEmitter::addp, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
|
||||
//5
|
||||
{"SUBR", 0x5000, 0xf800, DSPInterpreter::subr, &DSPEmitter::subr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG18, 1, 0, 9, 0x0600}}, true, false, false, false, true},
|
||||
{"SUBAX", 0x5800, 0xfc00, DSPInterpreter::subax, &DSPEmitter::subax, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_AX, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"SUB", 0x5c00, 0xfe00, DSPInterpreter::sub, &DSPEmitter::sub, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACC_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"SUBP", 0x5e00, 0xfe00, DSPInterpreter::subp, &DSPEmitter::subp, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
|
||||
//6
|
||||
{"MOVR", 0x6000, 0xf800, DSPInterpreter::movr, &DSPEmitter::movr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG18, 1, 0, 9, 0x0600}}, true, false, false, false, true},
|
||||
{"MOVAX", 0x6800, 0xfc00, DSPInterpreter::movax, &DSPEmitter::movax, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_AX, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"MOV", 0x6c00, 0xfe00, DSPInterpreter::mov, &DSPEmitter::mov, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACC_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MOVP", 0x6e00, 0xfe00, DSPInterpreter::movp, &DSPEmitter::movp, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
|
||||
//7
|
||||
{"ADDAXL", 0x7000, 0xfc00, DSPInterpreter::addaxl, &DSPEmitter::addaxl, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG18, 1, 0, 9, 0x0200}}, true, false, false, false, true},
|
||||
{"INCM", 0x7400, 0xfe00, DSPInterpreter::incm, &DSPEmitter::incm, 1, 1, {{P_ACCM, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"INC", 0x7600, 0xfe00, DSPInterpreter::inc, &DSPEmitter::inc, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"DECM", 0x7800, 0xfe00, DSPInterpreter::decm, &DSPEmitter::decm, 1, 1, {{P_ACCM, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"DEC", 0x7a00, 0xfe00, DSPInterpreter::dec, &DSPEmitter::dec, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"NEG", 0x7c00, 0xfe00, DSPInterpreter::neg, &DSPEmitter::neg, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MOVNP", 0x7e00, 0xfe00, DSPInterpreter::movnp, &DSPEmitter::movnp, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
|
||||
//8
|
||||
{"NX", 0x8000, 0xf700, DSPInterpreter::nx, &DSPEmitter::nx, 1, 0, {}, true, false, false, false, false},
|
||||
{"CLR", 0x8100, 0xf700, DSPInterpreter::clr, &DSPEmitter::clr, 1, 1, {{P_ACC, 1, 0, 11, 0x0800}}, true, false, false, false, true},
|
||||
{"CMP", 0x8200, 0xff00, DSPInterpreter::cmp, &DSPEmitter::cmp, 1, 0, {}, true, false, false, false, true},
|
||||
{"MULAXH", 0x8300, 0xff00, DSPInterpreter::mulaxh, &DSPEmitter::mulaxh, 1, 0, {}, true, false, false, false, true},
|
||||
{"CLRP", 0x8400, 0xff00, DSPInterpreter::clrp, &DSPEmitter::clrp, 1, 0, {}, true, false, false, false, true},
|
||||
{"TSTPROD", 0x8500, 0xff00, DSPInterpreter::tstprod, &DSPEmitter::tstprod,1, 0, {}, true, false, false, false, true},
|
||||
{"TSTAXH", 0x8600, 0xfe00, DSPInterpreter::tstaxh, &DSPEmitter::tstaxh, 1, 1, {{P_REG1A, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"M2", 0x8a00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
|
||||
{"M0", 0x8b00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
|
||||
{"CLR15", 0x8c00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
|
||||
{"SET15", 0x8d00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
|
||||
{"SET16", 0x8e00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
|
||||
{"SET40", 0x8f00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
|
||||
|
||||
//9
|
||||
{"MUL", 0x9000, 0xf700, DSPInterpreter::mul, &DSPEmitter::mul, 1, 2, {{P_REG18, 1, 0, 11, 0x0800}, {P_REG1A, 1, 0, 11, 0x0800}}, true, false, false, false, true},
|
||||
{"ASR16", 0x9100, 0xf700, DSPInterpreter::asr16, &DSPEmitter::asr16, 1, 1, {{P_ACC, 1, 0, 11, 0x0800}}, true, false, false, false, true},
|
||||
{"MULMVZ", 0x9200, 0xf600, DSPInterpreter::mulmvz, &DSPEmitter::mulmvz, 1, 3, {{P_REG18, 1, 0, 11, 0x0800}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MULAC", 0x9400, 0xf600, DSPInterpreter::mulac, &DSPEmitter::mulac, 1, 3, {{P_REG18, 1, 0, 11, 0x0800}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MULMV", 0x9600, 0xf600, DSPInterpreter::mulmv, &DSPEmitter::mulmv, 1, 3, {{P_REG18, 1, 0, 11, 0x0800}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
|
||||
//a-b
|
||||
{"MULX", 0xa000, 0xe700, DSPInterpreter::mulx, &DSPEmitter::mulx, 1, 2, {{P_REGM18, 1, 0, 11, 0x1000}, {P_REGM19, 1, 0, 10, 0x0800}}, true, false, false, false, true},
|
||||
{"ABS", 0xa100, 0xf700, DSPInterpreter::abs, &DSPEmitter::abs, 1, 1, {{P_ACC, 1, 0, 11, 0x0800}}, true, false, false, false, true},
|
||||
{"MULXMVZ", 0xa200, 0xe600, DSPInterpreter::mulxmvz, &DSPEmitter::mulxmvz,1, 3, {{P_REGM18, 1, 0, 11, 0x1000}, {P_REGM19, 1, 0, 10, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MULXAC", 0xa400, 0xe600, DSPInterpreter::mulxac, &DSPEmitter::mulxac, 1, 3, {{P_REGM18, 1, 0, 11, 0x1000}, {P_REGM19, 1, 0, 10, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MULXMV", 0xa600, 0xe600, DSPInterpreter::mulxmv, &DSPEmitter::mulxmv, 1, 3, {{P_REGM18, 1, 0, 11, 0x1000}, {P_REGM19, 1, 0, 10, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"TST", 0xb100, 0xf700, DSPInterpreter::tst, &DSPEmitter::tst, 1, 1, {{P_ACC, 1, 0, 11, 0x0800}}, true, false, false, false, true},
|
||||
|
||||
//c-d
|
||||
{"MULC", 0xc000, 0xe700, DSPInterpreter::mulc, &DSPEmitter::mulc, 1, 2, {{P_ACCM, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}}, true, false, false, false, true},
|
||||
{"CMPAR" , 0xc100, 0xe700, DSPInterpreter::cmpar, &DSPEmitter::cmpar, 1, 2, {{P_ACC, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}}, true, false, false, false, true},
|
||||
{"MULCMVZ", 0xc200, 0xe600, DSPInterpreter::mulcmvz, &DSPEmitter::mulcmvz,1, 3, {{P_ACCM, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MULCAC", 0xc400, 0xe600, DSPInterpreter::mulcac, &DSPEmitter::mulcac, 1, 3, {{P_ACCM, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MULCMV", 0xc600, 0xe600, DSPInterpreter::mulcmv, &DSPEmitter::mulcmv, 1, 3, {{P_ACCM, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
|
||||
//e
|
||||
{"MADDX", 0xe000, 0xfc00, DSPInterpreter::maddx, &DSPEmitter::maddx, 1, 2, {{P_REGM18, 1, 0, 8, 0x0200}, {P_REGM19, 1, 0, 7, 0x0100}}, true, false, false, false, true},
|
||||
{"MSUBX", 0xe400, 0xfc00, DSPInterpreter::msubx, &DSPEmitter::msubx, 1, 2, {{P_REGM18, 1, 0, 8, 0x0200}, {P_REGM19, 1, 0, 7, 0x0100}}, true, false, false, false, true},
|
||||
{"MADDC", 0xe800, 0xfc00, DSPInterpreter::maddc, &DSPEmitter::maddc, 1, 2, {{P_ACCM, 1, 0, 9, 0x0200}, {P_REG19, 1, 0, 7, 0x0100}}, true, false, false, false, true},
|
||||
{"MSUBC", 0xec00, 0xfc00, DSPInterpreter::msubc, &DSPEmitter::msubc, 1, 2, {{P_ACCM, 1, 0, 9, 0x0200}, {P_REG19, 1, 0, 7, 0x0100}}, true, false, false, false, true},
|
||||
|
||||
//f
|
||||
{"LSL16", 0xf000, 0xfe00, DSPInterpreter::lsl16, &DSPEmitter::lsl16, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MADD", 0xf200, 0xfe00, DSPInterpreter::madd, &DSPEmitter::madd, 1, 2, {{P_REG18, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"LSR16", 0xf400, 0xfe00, DSPInterpreter::lsr16, &DSPEmitter::lsr16, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"MSUB", 0xf600, 0xfe00, DSPInterpreter::msub, &DSPEmitter::msub, 1, 2, {{P_REG18, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"ADDPAXZ", 0xf800, 0xfc00, DSPInterpreter::addpaxz, &DSPEmitter::addpaxz,1, 2, {{P_ACC, 1, 0, 9, 0x0200}, {P_AX, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
{"CLRL", 0xfc00, 0xfe00, DSPInterpreter::clrl, &DSPEmitter::clrl, 1, 1, {{P_ACCL, 1, 0, 11, 0x0800}}, true, false, false, false, true},
|
||||
{"MOVPZ", 0xfe00, 0xfe00, DSPInterpreter::movpz, &DSPEmitter::movpz, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
|
||||
};
|
||||
|
||||
const DSPOPCTemplate cw =
|
||||
{"CW", 0x0000, 0x0000, NULL, NULL, 1, 1, {{P_VAL, 2, 0, 0, 0xffff}}, false, false};
|
||||
|
||||
// extended opcodes
|
||||
|
||||
const DSPOPCTemplate opcodes_ext[] =
|
||||
{
|
||||
{"XXX", 0x0000, 0x00fc, DSPInterpreter::Ext::nop, &DSPEmitter::nop, 1, 1, {{P_VAL, 1, 0, 0, 0x00ff}}, false, false, false},
|
||||
|
||||
{"DR", 0x0004, 0x00fc, DSPInterpreter::Ext::dr, &DSPEmitter::dr, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
{"IR", 0x0008, 0x00fc, DSPInterpreter::Ext::ir, &DSPEmitter::ir, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
{"NR", 0x000c, 0x00fc, DSPInterpreter::Ext::nr, &DSPEmitter::nr, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
{"MV", 0x0010, 0x00f0, DSPInterpreter::Ext::mv, &DSPEmitter::mv, 1, 2, {{P_REG18, 1, 0, 2, 0x000c}, {P_REG1C, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
|
||||
{"S", 0x0020, 0x00e4, DSPInterpreter::Ext::s, &DSPEmitter::s, 1, 2, {{P_PRG, 1, 0, 0, 0x0003}, {P_REG1C, 1, 0, 3, 0x0018}}, false, false, false},
|
||||
{"SN", 0x0024, 0x00e4, DSPInterpreter::Ext::sn, &DSPEmitter::sn, 1, 2, {{P_PRG, 1, 0, 0, 0x0003}, {P_REG1C, 1, 0, 3, 0x0018}}, false, false, false},
|
||||
|
||||
{"L", 0x0040, 0x00c4, DSPInterpreter::Ext::l, &DSPEmitter::l, 1, 2, {{P_REG18, 1, 0, 3, 0x0038}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
{"LN", 0x0044, 0x00c4, DSPInterpreter::Ext::ln, &DSPEmitter::ln, 1, 2, {{P_REG18, 1, 0, 3, 0x0038}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
|
||||
{"LS", 0x0080, 0x00ce, DSPInterpreter::Ext::ls, &DSPEmitter::ls, 1, 2, {{P_REG18, 1, 0, 4, 0x0030}, {P_ACCM, 1, 0, 0, 0x0001}}, false, false, false},
|
||||
{"SL", 0x0082, 0x00ce, DSPInterpreter::Ext::sl, &DSPEmitter::sl, 1, 2, {{P_ACCM, 1, 0, 0, 0x0001}, {P_REG18, 1, 0, 4, 0x0030}}, false, false, false},
|
||||
{"LSN", 0x0084, 0x00ce, DSPInterpreter::Ext::lsn, &DSPEmitter::lsn, 1, 2, {{P_REG18, 1, 0, 4, 0x0030}, {P_ACCM, 1, 0, 0, 0x0001}}, false, false, false},
|
||||
{"SLN", 0x0086, 0x00ce, DSPInterpreter::Ext::sln, &DSPEmitter::sln, 1, 2, {{P_ACCM, 1, 0, 0, 0x0001}, {P_REG18, 1, 0, 4, 0x0030}}, false, false, false},
|
||||
{"LSM", 0x0088, 0x00ce, DSPInterpreter::Ext::lsm, &DSPEmitter::lsm, 1, 2, {{P_REG18, 1, 0, 4, 0x0030}, {P_ACCM, 1, 0, 0, 0x0001}}, false, false, false},
|
||||
{"SLM", 0x008a, 0x00ce, DSPInterpreter::Ext::slm, &DSPEmitter::slm, 1, 2, {{P_ACCM, 1, 0, 0, 0x0001}, {P_REG18, 1, 0, 4, 0x0030}}, false, false, false},
|
||||
{"LSNM", 0x008c, 0x00ce, DSPInterpreter::Ext::lsnm, &DSPEmitter::lsnm, 1, 2, {{P_REG18, 1, 0, 4, 0x0030}, {P_ACCM, 1, 0, 0, 0x0001}}, false, false, false},
|
||||
{"SLNM", 0x008e, 0x00ce, DSPInterpreter::Ext::slnm, &DSPEmitter::slnm, 1, 2, {{P_ACCM, 1, 0, 0, 0x0001}, {P_REG18, 1, 0, 4, 0x0030}}, false, false, false},
|
||||
|
||||
{"LD", 0x00c0, 0x00cc, DSPInterpreter::Ext::ld, &DSPEmitter::ld, 1, 3, {{P_REGM18, 1, 0, 4, 0x0020}, {P_REGM19, 1, 0, 3, 0x0010}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
{"LDN", 0x00c4, 0x00cc, DSPInterpreter::Ext::ldn, &DSPEmitter::ldn, 1, 3, {{P_REGM18, 1, 0, 4, 0x0020}, {P_REGM19, 1, 0, 3, 0x0010}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
{"LDM", 0x00c8, 0x00cc, DSPInterpreter::Ext::ldm, &DSPEmitter::ldm, 1, 3, {{P_REGM18, 1, 0, 4, 0x0020}, {P_REGM19, 1, 0, 3, 0x0010}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
{"LDNM", 0x00cc, 0x00cc, DSPInterpreter::Ext::ldnm, &DSPEmitter::ldnm, 1, 3, {{P_REGM18, 1, 0, 4, 0x0020}, {P_REGM19, 1, 0, 3, 0x0010}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
|
||||
};
|
||||
|
||||
const int opcodes_size = sizeof(opcodes) / sizeof(DSPOPCTemplate);
|
||||
const int opcodes_ext_size = sizeof(opcodes_ext) / sizeof(DSPOPCTemplate);
|
||||
|
||||
const pdlabel_t pdlabels[] =
|
||||
{
|
||||
{0xffa0, "COEF_A1_0", "COEF_A1_0",},
|
||||
{0xffa1, "COEF_A2_0", "COEF_A2_0",},
|
||||
{0xffa2, "COEF_A1_1", "COEF_A1_1",},
|
||||
{0xffa3, "COEF_A2_1", "COEF_A2_1",},
|
||||
{0xffa4, "COEF_A1_2", "COEF_A1_2",},
|
||||
{0xffa5, "COEF_A2_2", "COEF_A2_2",},
|
||||
{0xffa6, "COEF_A1_3", "COEF_A1_3",},
|
||||
{0xffa7, "COEF_A2_3", "COEF_A2_3",},
|
||||
{0xffa8, "COEF_A1_4", "COEF_A1_4",},
|
||||
{0xffa9, "COEF_A2_4", "COEF_A2_4",},
|
||||
{0xffaa, "COEF_A1_5", "COEF_A1_5",},
|
||||
{0xffab, "COEF_A2_5", "COEF_A2_5",},
|
||||
{0xffac, "COEF_A1_6", "COEF_A1_6",},
|
||||
{0xffad, "COEF_A2_6", "COEF_A2_6",},
|
||||
{0xffae, "COEF_A1_7", "COEF_A1_7",},
|
||||
{0xffaf, "COEF_A2_7", "COEF_A2_7",},
|
||||
|
||||
{0xffb0, "0xffb0", 0,},
|
||||
{0xffb1, "0xffb1", 0,},
|
||||
{0xffb2, "0xffb2", 0,},
|
||||
{0xffb3, "0xffb3", 0,},
|
||||
{0xffb4, "0xffb4", 0,},
|
||||
{0xffb5, "0xffb5", 0,},
|
||||
{0xffb6, "0xffb6", 0,},
|
||||
{0xffb7, "0xffb7", 0,},
|
||||
{0xffb8, "0xffb8", 0,},
|
||||
{0xffb9, "0xffb9", 0,},
|
||||
{0xffba, "0xffba", 0,},
|
||||
{0xffbb, "0xffbb", 0,},
|
||||
{0xffbc, "0xffbc", 0,},
|
||||
{0xffbd, "0xffbd", 0,},
|
||||
{0xffbe, "0xffbe", 0,},
|
||||
{0xffbf, "0xffbf", 0,},
|
||||
|
||||
{0xffc0, "0xffc0", 0,},
|
||||
{0xffc1, "0xffc1", 0,},
|
||||
{0xffc2, "0xffc2", 0,},
|
||||
{0xffc3, "0xffc3", 0,},
|
||||
{0xffc4, "0xffc4", 0,},
|
||||
{0xffc5, "0xffc5", 0,},
|
||||
{0xffc6, "0xffc6", 0,},
|
||||
{0xffc7, "0xffc7", 0,},
|
||||
{0xffc8, "0xffc8", 0,},
|
||||
{0xffc9, "DSCR", "DSP DMA Control Reg",},
|
||||
{0xffca, "0xffca", 0,},
|
||||
{0xffcb, "DSBL", "DSP DMA Block Length",},
|
||||
{0xffcc, "0xffcc", 0,},
|
||||
{0xffcd, "DSPA", "DSP DMA DMEM Address",},
|
||||
{0xffce, "DSMAH", "DSP DMA Mem Address H",},
|
||||
{0xffcf, "DSMAL", "DSP DMA Mem Address L",},
|
||||
|
||||
{0xffd0, "0xffd0",0,},
|
||||
{0xffd1, "SampleFormat", "SampleFormat",},
|
||||
{0xffd2, "0xffd2",0,},
|
||||
{0xffd3, "UnkZelda", "Unk Zelda reads/writes from/to it",},
|
||||
{0xffd4, "ACSAH", "Accelerator start address H",},
|
||||
{0xffd5, "ACSAL", "Accelerator start address L",},
|
||||
{0xffd6, "ACEAH", "Accelerator end address H",},
|
||||
{0xffd7, "ACEAL", "Accelerator end address L",},
|
||||
{0xffd8, "ACCAH", "Accelerator current address H",},
|
||||
{0xffd9, "ACCAL", "Accelerator current address L",},
|
||||
{0xffda, "pred_scale", "pred_scale",},
|
||||
{0xffdb, "yn1", "yn1",},
|
||||
{0xffdc, "yn2", "yn2",},
|
||||
{0xffdd, "ARAM", "Direct Read from ARAM (uses ADPCM)",},
|
||||
{0xffde, "GAIN", "Gain",},
|
||||
{0xffdf, "0xffdf", 0,},
|
||||
|
||||
{0xffe0, "0xffe0",0,},
|
||||
{0xffe1, "0xffe1",0,},
|
||||
{0xffe2, "0xffe2",0,},
|
||||
{0xffe3, "0xffe3",0,},
|
||||
{0xffe4, "0xffe4",0,},
|
||||
{0xffe5, "0xffe5",0,},
|
||||
{0xffe6, "0xffe6",0,},
|
||||
{0xffe7, "0xffe7",0,},
|
||||
{0xffe8, "0xffe8",0,},
|
||||
{0xffe9, "0xffe9",0,},
|
||||
{0xffea, "0xffea",0,},
|
||||
{0xffeb, "0xffeb",0,},
|
||||
{0xffec, "0xffec",0,},
|
||||
{0xffed, "0xffed",0,},
|
||||
{0xffee, "0xffee",0,},
|
||||
{0xffef, "AMDM", "ARAM DMA Request Mask",},
|
||||
|
||||
{0xfff0, "0xfff0",0,},
|
||||
{0xfff1, "0xfff1",0,},
|
||||
{0xfff2, "0xfff2",0,},
|
||||
{0xfff3, "0xfff3",0,},
|
||||
{0xfff4, "0xfff4",0,},
|
||||
{0xfff5, "0xfff5",0,},
|
||||
{0xfff6, "0xfff6",0,},
|
||||
{0xfff7, "0xfff7",0,},
|
||||
{0xfff8, "0xfff8",0,},
|
||||
{0xfff9, "0xfff9",0,},
|
||||
{0xfffa, "0xfffa",0,},
|
||||
{0xfffb, "DIRQ", "DSP IRQ Request",},
|
||||
{0xfffc, "DMBH", "DSP Mailbox H",},
|
||||
{0xfffd, "DMBL", "DSP Mailbox L",},
|
||||
{0xfffe, "CMBH", "CPU Mailbox H",},
|
||||
{0xffff, "CMBL", "CPU Mailbox L",},
|
||||
};
|
||||
|
||||
const u32 pdlabels_size = sizeof(pdlabels) / sizeof(pdlabel_t);
|
||||
|
||||
const pdlabel_t regnames[] =
|
||||
{
|
||||
{0x00, "AR0", "Addr Reg 00",},
|
||||
{0x01, "AR1", "Addr Reg 01",},
|
||||
{0x02, "AR2", "Addr Reg 02",},
|
||||
{0x03, "AR3", "Addr Reg 03",},
|
||||
{0x04, "IX0", "Index Reg 0",},
|
||||
{0x05, "IX1", "Index Reg 1",},
|
||||
{0x06, "IX2", "Index Reg 2",},
|
||||
{0x07, "IX3", "Index Reg 3",},
|
||||
{0x08, "WR0", "Wrapping Register 0",},
|
||||
{0x09, "WR1", "Wrapping Register 1",},
|
||||
{0x0a, "WR2", "Wrapping Register 2",},
|
||||
{0x0b, "WR3", "Wrapping Register 3",},
|
||||
{0x0c, "ST0", "Call stack",},
|
||||
{0x0d, "ST1", "Data stack",},
|
||||
{0x0e, "ST2", "Loop addr stack",},
|
||||
{0x0f, "ST3", "Loop counter",},
|
||||
{0x10, "AC0.H", "Accu High 0",},
|
||||
{0x11, "AC1.H", "Accu High 1",},
|
||||
{0x12, "CR", "Config Register",},
|
||||
{0x13, "SR", "Special Register",},
|
||||
{0x14, "PROD.L", "Prod L",},
|
||||
{0x15, "PROD.M1", "Prod M1",},
|
||||
{0x16, "PROD.H", "Prod H",},
|
||||
{0x17, "PROD.M2", "Prod M2",},
|
||||
{0x18, "AX0.L", "Extra Accu L 0",},
|
||||
{0x19, "AX1.L", "Extra Accu L 1",},
|
||||
{0x1a, "AX0.H", "Extra Accu H 0",},
|
||||
{0x1b, "AX1.H", "Extra Accu H 1",},
|
||||
{0x1c, "AC0.L", "Accu Low 0",},
|
||||
{0x1d, "AC1.L", "Accu Low 1",},
|
||||
{0x1e, "AC0.M", "Accu Mid 0",},
|
||||
{0x1f, "AC1.M", "Accu Mid 1",},
|
||||
|
||||
// To resolve combined register names.
|
||||
{0x20, "ACC0", "Accu Full 0",},
|
||||
{0x21, "ACC1", "Accu Full 1",},
|
||||
{0x22, "AX0", "Extra Accu 0",},
|
||||
{0x23, "AX1", "Extra Accu 1",},
|
||||
};
|
||||
|
||||
const DSPOPCTemplate *opTable[OPTABLE_SIZE];
|
||||
const DSPOPCTemplate *extOpTable[EXT_OPTABLE_SIZE];
|
||||
u16 writeBackLog[WRITEBACKLOGSIZE];
|
||||
int writeBackLogIdx[WRITEBACKLOGSIZE];
|
||||
|
||||
const char* pdname(u16 val)
|
||||
{
|
||||
static char tmpstr[12]; // nasty
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(pdlabels) / sizeof(pdlabel_t)); i++)
|
||||
{
|
||||
if (pdlabels[i].addr == val)
|
||||
return pdlabels[i].name;
|
||||
}
|
||||
|
||||
sprintf(tmpstr, "0x%04x", val);
|
||||
return tmpstr;
|
||||
}
|
||||
|
||||
const char *pdregname(int val)
|
||||
{
|
||||
return regnames[val].name;
|
||||
}
|
||||
|
||||
const char *pdregnamelong(int val)
|
||||
{
|
||||
return regnames[val].description;
|
||||
}
|
||||
|
||||
const DSPOPCTemplate *GetOpTemplate(const UDSPInstruction &inst)
|
||||
{
|
||||
return opTable[inst];
|
||||
}
|
||||
|
||||
|
||||
// This function could use the above GetOpTemplate, but then we'd lose the
|
||||
// nice property that it catches colliding op masks.
|
||||
void InitInstructionTable()
|
||||
{
|
||||
// ext op table
|
||||
for (int i = 0; i < EXT_OPTABLE_SIZE; i++)
|
||||
extOpTable[i] = &cw;
|
||||
|
||||
for (int i = 0; i < EXT_OPTABLE_SIZE; i++)
|
||||
{
|
||||
for (int j = 0; j < opcodes_ext_size; j++)
|
||||
{
|
||||
u16 mask = opcodes_ext[j].opcode_mask;
|
||||
if ((mask & i) == opcodes_ext[j].opcode)
|
||||
{
|
||||
if (extOpTable[i] == &cw)
|
||||
extOpTable[i] = &opcodes_ext[j];
|
||||
else
|
||||
ERROR_LOG(DSPLLE, "opcode ext table place %d already in use for %s", i, opcodes_ext[j].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// op table
|
||||
for (int i = 0; i < OPTABLE_SIZE; i++)
|
||||
opTable[i] = &cw;
|
||||
|
||||
for (int i = 0; i < OPTABLE_SIZE; i++)
|
||||
{
|
||||
for (int j = 0; j < opcodes_size; j++)
|
||||
{
|
||||
u16 mask = opcodes[j].opcode_mask;
|
||||
if ((mask & i) == opcodes[j].opcode)
|
||||
{
|
||||
if (opTable[i] == &cw)
|
||||
opTable[i] = &opcodes[j];
|
||||
else
|
||||
ERROR_LOG(DSPLLE, "opcode table place %d already in use for %s", i, opcodes[j].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i < WRITEBACKLOGSIZE; i++)
|
||||
writeBackLogIdx[i] = -1;
|
||||
}
|
||||
158
Source/Core/Core/Src/DSP/DSPTables.h
Normal file
158
Source/Core/Core/Src/DSP/DSPTables.h
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie (c) 2005 (duddie@walla.com)
|
||||
|
||||
#ifndef _DSPTABLES_H
|
||||
#define _DSPTABLES_H
|
||||
|
||||
//nclude "Common.h"
|
||||
#include "DSPEmitter.h"
|
||||
#include "DSPCommon.h"
|
||||
|
||||
// The non-ADDR ones that end with _D are the opposite one - if the bit specify
|
||||
// ACC0, then ACC_D will be ACC1.
|
||||
|
||||
// The values of these are very important.
|
||||
// For the reg ones, the value >> 8 is the base register.
|
||||
// & 0x80 means it's a "D".
|
||||
|
||||
enum partype_t
|
||||
{
|
||||
P_NONE = 0x0000,
|
||||
P_VAL = 0x0001,
|
||||
P_IMM = 0x0002,
|
||||
P_MEM = 0x0003,
|
||||
P_STR = 0x0004,
|
||||
P_ADDR_I = 0x0005,
|
||||
P_ADDR_D = 0x0006,
|
||||
P_REG = 0x8000,
|
||||
P_REG04 = P_REG | 0x0400, // IX
|
||||
P_REG08 = P_REG | 0x0800,
|
||||
P_REG18 = P_REG | 0x1800,
|
||||
P_REGM18 = P_REG | 0x1810, // used in multiply instructions
|
||||
P_REG19 = P_REG | 0x1900,
|
||||
P_REGM19 = P_REG | 0x1910, // used in multiply instructions
|
||||
P_REG1A = P_REG | 0x1a80,
|
||||
P_REG1C = P_REG | 0x1c00,
|
||||
// P_ACC = P_REG | 0x1c10, // used for global accum (gcdsptool's value)
|
||||
P_ACCL = P_REG | 0x1c00, // used for low part of accum
|
||||
P_ACCM = P_REG | 0x1e00, // used for mid part of accum
|
||||
// The following are not in gcdsptool
|
||||
P_ACCM_D = P_REG | 0x1e80,
|
||||
P_ACC = P_REG | 0x2000, // used for full accum.
|
||||
P_ACC_D = P_REG | 0x2080,
|
||||
P_AX = P_REG | 0x2200,
|
||||
P_REGS_MASK = 0x03f80, // gcdsptool's value = 0x01f80
|
||||
P_REF = P_REG | 0x4000,
|
||||
P_PRG = P_REF | P_REG,
|
||||
|
||||
// The following seem like junk:
|
||||
// P_REG10 = P_REG | 0x1000,
|
||||
// P_AX_D = P_REG | 0x2280,
|
||||
};
|
||||
|
||||
#define OPTABLE_SIZE 0xffff + 1
|
||||
#define EXT_OPTABLE_SIZE 0xff + 1
|
||||
|
||||
typedef void (*dspIntFunc)(const UDSPInstruction);
|
||||
typedef void (DSPEmitter::*dspJitFunc)(const UDSPInstruction);
|
||||
|
||||
struct param2_t
|
||||
{
|
||||
partype_t type;
|
||||
u8 size;
|
||||
u8 loc;
|
||||
s8 lshift;
|
||||
u16 mask;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
u16 opcode;
|
||||
u16 opcode_mask;
|
||||
|
||||
dspIntFunc intFunc;
|
||||
dspJitFunc jitFunc;
|
||||
|
||||
u8 size;
|
||||
u8 param_count;
|
||||
param2_t params[8];
|
||||
bool extended;
|
||||
bool branch;
|
||||
bool uncond_branch;
|
||||
bool reads_pc;
|
||||
bool updates_sr;
|
||||
} DSPOPCTemplate;
|
||||
|
||||
typedef DSPOPCTemplate opc_t;
|
||||
|
||||
// Opcodes
|
||||
extern const DSPOPCTemplate opcodes[];
|
||||
extern const int opcodes_size;
|
||||
extern const DSPOPCTemplate opcodes_ext[];
|
||||
extern const int opcodes_ext_size;
|
||||
extern const DSPOPCTemplate cw;
|
||||
|
||||
#define WRITEBACKLOGSIZE 5
|
||||
|
||||
extern const DSPOPCTemplate *opTable[OPTABLE_SIZE];
|
||||
extern const DSPOPCTemplate *extOpTable[EXT_OPTABLE_SIZE];
|
||||
extern u16 writeBackLog[WRITEBACKLOGSIZE];
|
||||
extern int writeBackLogIdx[WRITEBACKLOGSIZE];
|
||||
|
||||
// Predefined labels
|
||||
struct pdlabel_t
|
||||
{
|
||||
u16 addr;
|
||||
const char* name;
|
||||
const char* description;
|
||||
};
|
||||
|
||||
extern const pdlabel_t regnames[];
|
||||
extern const pdlabel_t pdlabels[];
|
||||
extern const u32 pdlabels_size;
|
||||
|
||||
const char *pdname(u16 val);
|
||||
const char *pdregname(int val);
|
||||
const char *pdregnamelong(int val);
|
||||
|
||||
void InitInstructionTable();
|
||||
void applyWriteBackLog();
|
||||
void zeroWriteBackLog();
|
||||
void zeroWriteBackLogPreserveAcc(u8 acc);
|
||||
|
||||
const DSPOPCTemplate *GetOpTemplate(const UDSPInstruction &inst);
|
||||
|
||||
inline void ExecuteInstruction(const UDSPInstruction inst)
|
||||
{
|
||||
const DSPOPCTemplate *tinst = GetOpTemplate(inst);
|
||||
|
||||
if (tinst->extended) {
|
||||
if ((inst >> 12) == 0x3)
|
||||
extOpTable[inst & 0x7F]->intFunc(inst);
|
||||
else
|
||||
extOpTable[inst & 0xFF]->intFunc(inst);
|
||||
}
|
||||
tinst->intFunc(inst);
|
||||
if (tinst->extended) {
|
||||
applyWriteBackLog();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _DSPTABLES_H
|
||||
1152
Source/Core/Core/Src/DSP/DspIntArithmetic.cpp
Normal file
1152
Source/Core/Core/Src/DSP/DspIntArithmetic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
275
Source/Core/Core/Src/DSP/DspIntBranch.cpp
Normal file
275
Source/Core/Core/Src/DSP/DspIntBranch.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPMemoryMap.h"
|
||||
#include "DSPStacks.h"
|
||||
|
||||
#include "DSPIntCCUtil.h"
|
||||
#include "DSPIntUtil.h"
|
||||
|
||||
namespace DSPInterpreter {
|
||||
|
||||
// Generic call implementation
|
||||
// CALLcc addressA
|
||||
// 0000 0010 1011 cccc
|
||||
// aaaa aaaa aaaa aaaa
|
||||
// Call function if condition cc has been met. Push program counter of
|
||||
// instruction following "call" to $st0. Set program counter to address
|
||||
// represented by value that follows this "call" instruction.
|
||||
void call(const UDSPInstruction opc)
|
||||
{
|
||||
// must be outside the if.
|
||||
u16 dest = dsp_fetch_code();
|
||||
if (CheckCondition(opc & 0xf))
|
||||
{
|
||||
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
|
||||
g_dsp.pc = dest;
|
||||
}
|
||||
}
|
||||
|
||||
// Generic callr implementation
|
||||
// CALLRcc $R
|
||||
// 0001 0111 rrr1 cccc
|
||||
// Call function if condition cc has been met. Push program counter of
|
||||
// instruction following "call" to call stack $st0. Set program counter to
|
||||
// register $R.
|
||||
void callr(const UDSPInstruction opc)
|
||||
{
|
||||
if (CheckCondition(opc & 0xf))
|
||||
{
|
||||
u8 reg = (opc >> 5) & 0x7;
|
||||
u16 addr = dsp_op_read_reg(reg);
|
||||
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
|
||||
g_dsp.pc = addr;
|
||||
}
|
||||
}
|
||||
|
||||
// Generic if implementation
|
||||
// IFcc
|
||||
// 0000 0010 0111 cccc
|
||||
// Execute following opcode if the condition has been met.
|
||||
void ifcc(const UDSPInstruction opc)
|
||||
{
|
||||
if (!CheckCondition(opc & 0xf))
|
||||
{
|
||||
// skip the next opcode - we have to lookup its size.
|
||||
dsp_skip_inst();
|
||||
}
|
||||
}
|
||||
|
||||
// Generic jmp implementation
|
||||
// Jcc addressA
|
||||
// 0000 0010 1001 cccc
|
||||
// aaaa aaaa aaaa aaaa
|
||||
// Jump to addressA if condition cc has been met. Set program counter to
|
||||
// address represented by value that follows this "jmp" instruction.
|
||||
void jcc(const UDSPInstruction opc)
|
||||
{
|
||||
u16 dest = dsp_fetch_code();
|
||||
if (CheckCondition(opc & 0xf))
|
||||
{
|
||||
g_dsp.pc = dest;
|
||||
}
|
||||
}
|
||||
|
||||
// Generic jmpr implementation
|
||||
// JMPcc $R
|
||||
// 0001 0111 rrr0 cccc
|
||||
// Jump to address; set program counter to a value from register $R.
|
||||
void jmprcc(const UDSPInstruction opc)
|
||||
{
|
||||
if (CheckCondition(opc & 0xf))
|
||||
{
|
||||
u8 reg = (opc >> 5) & 0x7;
|
||||
g_dsp.pc = dsp_op_read_reg(reg);
|
||||
}
|
||||
}
|
||||
|
||||
// Generic ret implementation
|
||||
// RETcc
|
||||
// 0000 0010 1101 cccc
|
||||
// Return from subroutine if condition cc has been met. Pops stored PC
|
||||
// from call stack $st0 and sets $pc to this location.
|
||||
void ret(const UDSPInstruction opc)
|
||||
{
|
||||
if (CheckCondition(opc & 0xf))
|
||||
{
|
||||
g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
|
||||
}
|
||||
}
|
||||
|
||||
// RTI
|
||||
// 0000 0010 1111 1111
|
||||
// Return from exception. Pops stored status register $sr from data stack
|
||||
// $st1 and program counter PC from call stack $st0 and sets $pc to this
|
||||
// location.
|
||||
void rti(const UDSPInstruction opc)
|
||||
{
|
||||
g_dsp.r.sr = dsp_reg_load_stack(DSP_STACK_D);
|
||||
g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
|
||||
|
||||
}
|
||||
|
||||
// HALT
|
||||
// 0000 0000 0020 0001
|
||||
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
|
||||
void halt(const UDSPInstruction opc)
|
||||
{
|
||||
g_dsp.cr |= 0x4;
|
||||
g_dsp.pc--;
|
||||
}
|
||||
|
||||
|
||||
// LOOP handling: Loop stack is used to control execution of repeated blocks of
|
||||
// instructions. Whenever there is value on stack $st2 and current PC is equal
|
||||
// value at $st2, then value at stack $st3 is decremented. If value is not zero
|
||||
// then PC is modified with value from call stack $st0. Otherwise values from
|
||||
// call stack $st0 and both loop stacks $st2 and $st3 are popped and execution
|
||||
// continues at next opcode.
|
||||
void HandleLoop()
|
||||
{
|
||||
// Handle looping hardware.
|
||||
const u16 rCallAddress = g_dsp.r.st[0];
|
||||
const u16 rLoopAddress = g_dsp.r.st[2];
|
||||
u16& rLoopCounter = g_dsp.r.st[3];
|
||||
|
||||
if (rLoopAddress > 0 && rLoopCounter > 0)
|
||||
{
|
||||
// FIXME: why -1? because we just read past it.
|
||||
if (g_dsp.pc - 1 == rLoopAddress)
|
||||
{
|
||||
rLoopCounter--;
|
||||
if (rLoopCounter > 0)
|
||||
{
|
||||
g_dsp.pc = rCallAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
// end of loop
|
||||
dsp_reg_load_stack(0);
|
||||
dsp_reg_load_stack(2);
|
||||
dsp_reg_load_stack(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// LOOP $R
|
||||
// 0000 0000 010r rrrr
|
||||
// Repeatedly execute following opcode until counter specified by value
|
||||
// from register $R reaches zero. Each execution decrement counter. Register
|
||||
// $R remains unchanged. If register $R is set to zero at the beginning of loop
|
||||
// then looped instruction will not get executed.
|
||||
// Actually, this instruction simply prepares the loop stacks for the above.
|
||||
// The looping hardware takes care of the rest.
|
||||
void loop(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x1f;
|
||||
u16 cnt = dsp_op_read_reg(reg);
|
||||
u16 loop_pc = g_dsp.pc;
|
||||
|
||||
if (cnt)
|
||||
{
|
||||
dsp_reg_store_stack(0, g_dsp.pc);
|
||||
dsp_reg_store_stack(2, loop_pc);
|
||||
dsp_reg_store_stack(3, cnt);
|
||||
}
|
||||
}
|
||||
|
||||
// LOOPI #I
|
||||
// 0001 0000 iiii iiii
|
||||
// Repeatedly execute following opcode until counter specified by
|
||||
// immediate value I reaches zero. Each execution decrement counter. If
|
||||
// immediate value I is set to zero at the beginning of loop then looped
|
||||
// instruction will not get executed.
|
||||
// Actually, this instruction simply prepares the loop stacks for the above.
|
||||
// The looping hardware takes care of the rest.
|
||||
void loopi(const UDSPInstruction opc)
|
||||
{
|
||||
u16 cnt = opc & 0xff;
|
||||
u16 loop_pc = g_dsp.pc;
|
||||
|
||||
if (cnt)
|
||||
{
|
||||
dsp_reg_store_stack(0, g_dsp.pc);
|
||||
dsp_reg_store_stack(2, loop_pc);
|
||||
dsp_reg_store_stack(3, cnt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BLOOP $R, addrA
|
||||
// 0000 0000 011r rrrr
|
||||
// aaaa aaaa aaaa aaaa
|
||||
// Repeatedly execute block of code starting at following opcode until
|
||||
// counter specified by value from register $R reaches zero. Block ends at
|
||||
// specified address addrA inclusive, ie. opcode at addrA is the last opcode
|
||||
// included in loop. Counter is pushed on loop stack $st3, end of block address
|
||||
// is pushed on loop stack $st2 and repeat address is pushed on call stack $st0.
|
||||
// Up to 4 nested loops are allowed.
|
||||
void bloop(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x1f;
|
||||
u16 cnt = dsp_op_read_reg(reg);
|
||||
u16 loop_pc = dsp_fetch_code();
|
||||
|
||||
if (cnt)
|
||||
{
|
||||
dsp_reg_store_stack(0, g_dsp.pc);
|
||||
dsp_reg_store_stack(2, loop_pc);
|
||||
dsp_reg_store_stack(3, cnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_dsp.pc = loop_pc;
|
||||
dsp_skip_inst();
|
||||
}
|
||||
}
|
||||
|
||||
// BLOOPI #I, addrA
|
||||
// 0001 0001 iiii iiii
|
||||
// aaaa aaaa aaaa aaaa
|
||||
// Repeatedly execute block of code starting at following opcode until
|
||||
// counter specified by immediate value I reaches zero. Block ends at specified
|
||||
// address addrA inclusive, ie. opcode at addrA is the last opcode included in
|
||||
// loop. Counter is pushed on loop stack $st3, end of block address is pushed
|
||||
// on loop stack $st2 and repeat address is pushed on call stack $st0. Up to 4
|
||||
// nested loops are allowed.
|
||||
void bloopi(const UDSPInstruction opc)
|
||||
{
|
||||
u16 cnt = opc & 0xff;
|
||||
u16 loop_pc = dsp_fetch_code();
|
||||
|
||||
if (cnt)
|
||||
{
|
||||
dsp_reg_store_stack(0, g_dsp.pc);
|
||||
dsp_reg_store_stack(2, loop_pc);
|
||||
dsp_reg_store_stack(3, cnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_dsp.pc = loop_pc;
|
||||
dsp_skip_inst();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
269
Source/Core/Core/Src/DSP/DspIntLoadStore.cpp
Normal file
269
Source/Core/Core/Src/DSP/DspIntLoadStore.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
#include "DSPInterpreter.h"
|
||||
|
||||
#include "DSPMemoryMap.h"
|
||||
#include "DSPIntUtil.h"
|
||||
|
||||
namespace DSPInterpreter {
|
||||
|
||||
// SRS @M, $(0x18+S)
|
||||
// 0010 1sss mmmm mmmm
|
||||
// Move value from register $(0x18+D) to data memory pointed by address
|
||||
// CR[0-7] | M. That is, the upper 8 bits of the address are the
|
||||
// bottom 8 bits from CR, and the lower 8 bits are from the 8-bit immediate.
|
||||
// Note: pc+=2 in duddie's doc seems wrong
|
||||
void srs(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = ((opc >> 8) & 0x7) + 0x18;
|
||||
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
|
||||
dsp_dmem_write(addr, dsp_op_read_reg(reg));
|
||||
}
|
||||
|
||||
// LRS $(0x18+D), @M
|
||||
// 0010 0ddd mmmm mmmm
|
||||
// Move value from data memory pointed by address CR[0-7] | M to register
|
||||
// $(0x18+D). That is, the upper 8 bits of the address are the bottom 8 bits
|
||||
// from CR, and the lower 8 bits are from the 8-bit immediate.
|
||||
void lrs(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = ((opc >> 8) & 0x7) + 0x18;
|
||||
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
|
||||
dsp_op_write_reg(reg, dsp_dmem_read(addr));
|
||||
dsp_conditional_extend_accum(reg);
|
||||
}
|
||||
|
||||
// LR $D, @M
|
||||
// 0000 0000 110d dddd
|
||||
// mmmm mmmm mmmm mmmm
|
||||
// Move value from data memory pointed by address M to register $D.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void lr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = opc & DSP_REG_MASK;
|
||||
u16 addr = dsp_fetch_code();
|
||||
u16 val = dsp_dmem_read(addr);
|
||||
dsp_op_write_reg(reg, val);
|
||||
dsp_conditional_extend_accum(reg);
|
||||
}
|
||||
|
||||
// SR @M, $S
|
||||
// 0000 0000 111s ssss
|
||||
// mmmm mmmm mmmm mmmm
|
||||
// Store value from register $S to a memory pointed by address M.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void sr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = opc & DSP_REG_MASK;
|
||||
u16 addr = dsp_fetch_code();
|
||||
u16 val = dsp_op_read_reg(reg);
|
||||
dsp_dmem_write(addr, val);
|
||||
}
|
||||
|
||||
// SI @M, #I
|
||||
// 0001 0110 mmmm mmmm
|
||||
// iiii iiii iiii iiii
|
||||
// Store 16-bit immediate value I to a memory location pointed by address
|
||||
// M (M is 8-bit value sign extended).
|
||||
void si(const UDSPInstruction opc)
|
||||
{
|
||||
u16 addr = (s8)opc;
|
||||
u16 imm = dsp_fetch_code();
|
||||
dsp_dmem_write(addr, imm);
|
||||
}
|
||||
|
||||
// LRR $D, @$S
|
||||
// 0001 1000 0ssd dddd
|
||||
// Move value from data memory pointed by addressing register $S to register $D.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void lrr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 5) & 0x3;
|
||||
u8 dreg = opc & 0x1f;
|
||||
|
||||
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
|
||||
dsp_op_write_reg(dreg, val);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
}
|
||||
|
||||
// LRRD $D, @$S
|
||||
// 0001 1000 1ssd dddd
|
||||
// Move value from data memory pointed by addressing register $S toregister $D.
|
||||
// Decrement register $S.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void lrrd(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 5) & 0x3;
|
||||
u8 dreg = opc & 0x1f;
|
||||
|
||||
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
|
||||
dsp_op_write_reg(dreg, val);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
g_dsp.r.ar[sreg] = dsp_decrement_addr_reg(sreg);
|
||||
}
|
||||
|
||||
// LRRI $D, @$S
|
||||
// 0001 1001 0ssd dddd
|
||||
// Move value from data memory pointed by addressing register $S to register $D.
|
||||
// Increment register $S.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void lrri(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 5) & 0x3;
|
||||
u8 dreg = opc & 0x1f;
|
||||
|
||||
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
|
||||
dsp_op_write_reg(dreg, val);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
g_dsp.r.ar[sreg] = dsp_increment_addr_reg(sreg);
|
||||
}
|
||||
|
||||
// LRRN $D, @$S
|
||||
// 0001 1001 1ssd dddd
|
||||
// Move value from data memory pointed by addressing register $S to register $D.
|
||||
// Add indexing register $(0x4+S) to register $S.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void lrrn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 5) & 0x3;
|
||||
u8 dreg = opc & 0x1f;
|
||||
|
||||
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
|
||||
dsp_op_write_reg(dreg, val);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
g_dsp.r.ar[sreg] = dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]);
|
||||
}
|
||||
|
||||
// SRR @$D, $S
|
||||
// 0001 1010 0dds ssss
|
||||
// Store value from source register $S to a memory location pointed by
|
||||
// addressing register $D.
|
||||
// FIXME: Perform additional operation depending on source register.
|
||||
void srr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x3;
|
||||
u8 sreg = opc & 0x1f;
|
||||
|
||||
u16 val = dsp_op_read_reg(sreg);
|
||||
dsp_dmem_write(g_dsp.r.ar[dreg], val);
|
||||
}
|
||||
|
||||
// SRRD @$D, $S
|
||||
// 0001 1010 1dds ssss
|
||||
// Store value from source register $S to a memory location pointed by
|
||||
// addressing register $D. Decrement register $D.
|
||||
// FIXME: Perform additional operation depending on source register.
|
||||
void srrd(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x3;
|
||||
u8 sreg = opc & 0x1f;
|
||||
|
||||
u16 val = dsp_op_read_reg(sreg);
|
||||
dsp_dmem_write(g_dsp.r.ar[dreg], val);
|
||||
g_dsp.r.ar[dreg] = dsp_decrement_addr_reg(dreg);
|
||||
}
|
||||
|
||||
// SRRI @$D, $S
|
||||
// 0001 1011 0dds ssss
|
||||
// Store value from source register $S to a memory location pointed by
|
||||
// addressing register $D. Increment register $D.
|
||||
// FIXME: Perform additional operation depending on source register.
|
||||
void srri(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x3;
|
||||
u8 sreg = opc & 0x1f;
|
||||
|
||||
u16 val = dsp_op_read_reg(sreg);
|
||||
dsp_dmem_write(g_dsp.r.ar[dreg], val);
|
||||
g_dsp.r.ar[dreg] = dsp_increment_addr_reg(dreg);
|
||||
}
|
||||
|
||||
// SRRN @$D, $S
|
||||
// 0001 1011 1dds ssss
|
||||
// Store value from source register $S to a memory location pointed by
|
||||
// addressing register $D. Add DSP_REG_IX0 register to register $D.
|
||||
// FIXME: Perform additional operation depending on source register.
|
||||
void srrn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x3;
|
||||
u8 sreg = opc & 0x1f;
|
||||
|
||||
u16 val = dsp_op_read_reg(sreg);
|
||||
dsp_dmem_write(g_dsp.r.ar[dreg], val);
|
||||
g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
|
||||
}
|
||||
|
||||
// ILRR $acD.m, @$arS
|
||||
// 0000 001d 0001 00ss
|
||||
// Move value from instruction memory pointed by addressing register
|
||||
// $arS to mid accumulator register $acD.m.
|
||||
void ilrr(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x3;
|
||||
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
|
||||
|
||||
g_dsp.r.ac[dreg-DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
}
|
||||
|
||||
// ILRRD $acD.m, @$arS
|
||||
// 0000 001d 0001 01ss
|
||||
// Move value from instruction memory pointed by addressing register
|
||||
// $arS to mid accumulator register $acD.m. Decrement addressing register $arS.
|
||||
void ilrrd(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x3;
|
||||
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
|
||||
|
||||
g_dsp.r.ac[dreg-DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
g_dsp.r.ar[reg] = dsp_decrement_addr_reg(reg);
|
||||
}
|
||||
|
||||
// ILRRI $acD.m, @$S
|
||||
// 0000 001d 0001 10ss
|
||||
// Move value from instruction memory pointed by addressing register
|
||||
// $arS to mid accumulator register $acD.m. Increment addressing register $arS.
|
||||
void ilrri(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x3;
|
||||
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
|
||||
|
||||
g_dsp.r.ac[dreg-DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
g_dsp.r.ar[reg] = dsp_increment_addr_reg(reg);
|
||||
}
|
||||
|
||||
// ILRRN $acD.m, @$arS
|
||||
// 0000 001d 0001 11ss
|
||||
// Move value from instruction memory pointed by addressing register
|
||||
// $arS to mid accumulator register $acD.m. Add corresponding indexing
|
||||
// register $ixS to addressing register $arS.
|
||||
void ilrrn(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x3;
|
||||
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
|
||||
|
||||
g_dsp.r.ac[dreg-DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
g_dsp.r.ar[reg] = dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
189
Source/Core/Core/Src/DSP/DspIntMisc.cpp
Normal file
189
Source/Core/Core/Src/DSP/DspIntMisc.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
|
||||
#include "DSPInterpreter.h"
|
||||
|
||||
#include "DSPCore.h"
|
||||
#include "DSPIntUtil.h"
|
||||
|
||||
namespace DSPInterpreter {
|
||||
|
||||
// MRR $D, $S
|
||||
// 0001 11dd ddds ssss
|
||||
// Move value from register $S to register $D.
|
||||
// todo: Perform additional operation depending on destination register.
|
||||
void mrr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1f;
|
||||
u8 dreg = (opc >> 5) & 0x1f;
|
||||
|
||||
u16 val = dsp_op_read_reg(sreg);
|
||||
dsp_op_write_reg(dreg, val);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
}
|
||||
|
||||
// LRI $D, #I
|
||||
// 0000 0000 100d dddd
|
||||
// iiii iiii iiii iiii
|
||||
// Load immediate value I to register $D.
|
||||
//
|
||||
// DSPSpy discovery: This, and possibly other instructions that load a
|
||||
// register, has a different behaviour in S40 mode if loaded to AC0.M: The
|
||||
// value gets sign extended to the whole accumulator! This does not happen in
|
||||
// S16 mode.
|
||||
void lri(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = opc & DSP_REG_MASK;
|
||||
u16 imm = dsp_fetch_code();
|
||||
dsp_op_write_reg(reg, imm);
|
||||
dsp_conditional_extend_accum(reg);
|
||||
}
|
||||
|
||||
// LRIS $(0x18+D), #I
|
||||
// 0000 1ddd iiii iiii
|
||||
// Load immediate value I (8-bit sign extended) to accumulator register.
|
||||
void lris(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
|
||||
u16 imm = (s8)opc;
|
||||
dsp_op_write_reg(reg, imm);
|
||||
dsp_conditional_extend_accum(reg);
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// NX
|
||||
// 1000 -000 xxxx xxxx
|
||||
// No operation, but can be extended with extended opcode.
|
||||
// This opcode is supposed to do nothing - it's used if you want to use
|
||||
// an opcode extension but not do anything. At least according to duddie.
|
||||
void nx(const UDSPInstruction opc)
|
||||
{
|
||||
zeroWriteBackLog();
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// DAR $arD
|
||||
// 0000 0000 0000 01dd
|
||||
// Decrement address register $arD.
|
||||
void dar(const UDSPInstruction opc)
|
||||
{
|
||||
g_dsp.r.ar[opc & 0x3] = dsp_decrement_addr_reg(opc & 0x3);
|
||||
}
|
||||
|
||||
// IAR $arD
|
||||
// 0000 0000 0000 10dd
|
||||
// Increment address register $arD.
|
||||
void iar(const UDSPInstruction opc)
|
||||
{
|
||||
g_dsp.r.ar[opc & 0x3] = dsp_increment_addr_reg(opc & 0x3);
|
||||
}
|
||||
|
||||
// SUBARN $arD
|
||||
// 0000 0000 0000 11dd
|
||||
// Subtract indexing register $ixD from an addressing register $arD.
|
||||
// used only in IPL-NTSC ucode
|
||||
void subarn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = opc & 0x3;
|
||||
g_dsp.r.ar[dreg] = dsp_decrease_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
|
||||
}
|
||||
|
||||
// ADDARN $arD, $ixS
|
||||
// 0000 0000 0001 ssdd
|
||||
// Adds indexing register $ixS to an addressing register $arD.
|
||||
// It is critical for the Zelda ucode that this one wraps correctly.
|
||||
void addarn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = opc & 0x3;
|
||||
u8 sreg = (opc >> 2) & 0x3;
|
||||
g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[sreg]);
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// SBCLR #I
|
||||
// 0001 0011 aaaa aiii
|
||||
// bit of status register $sr. Bit number is calculated by adding 6 to
|
||||
// immediate value I.
|
||||
void sbclr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 bit = (opc & 0x7) + 6;
|
||||
g_dsp.r.sr &= ~(1 << bit);
|
||||
}
|
||||
|
||||
// SBSET #I
|
||||
// 0001 0010 aaaa aiii
|
||||
// Set bit of status register $sr. Bit number is calculated by adding 6 to
|
||||
// immediate value I.
|
||||
void sbset(const UDSPInstruction opc)
|
||||
{
|
||||
u8 bit = (opc & 0x7) + 6;
|
||||
g_dsp.r.sr |= (1 << bit);
|
||||
}
|
||||
|
||||
// This is a bunch of flag setters, flipping bits in SR. So far so good,
|
||||
// but it's harder to know exactly what effect they have.
|
||||
void srbith(const UDSPInstruction opc)
|
||||
{
|
||||
zeroWriteBackLog();
|
||||
switch ((opc >> 8) & 0xf)
|
||||
{
|
||||
// M0/M2 change the multiplier mode (it can multiply by 2 for free).
|
||||
case 0xa: // M2
|
||||
g_dsp.r.sr &= ~SR_MUL_MODIFY;
|
||||
break;
|
||||
case 0xb: // M0
|
||||
g_dsp.r.sr |= SR_MUL_MODIFY;
|
||||
break;
|
||||
|
||||
// If set, treat multiplicands as unsigned.
|
||||
// If clear, treat them as signed.
|
||||
case 0xc: // CLR15
|
||||
g_dsp.r.sr &= ~SR_MUL_UNSIGNED;
|
||||
break;
|
||||
case 0xd: // SET15
|
||||
g_dsp.r.sr |= SR_MUL_UNSIGNED;
|
||||
break;
|
||||
|
||||
// Automatic 40-bit sign extension when loading ACx.M.
|
||||
// SET40 changes something very important: see the LRI instruction above.
|
||||
case 0xe: // SET16 (CLR40)
|
||||
g_dsp.r.sr &= ~SR_40_MODE_BIT;
|
||||
break;
|
||||
|
||||
case 0xf: // SET40
|
||||
g_dsp.r.sr |= SR_40_MODE_BIT;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
void unknown(const UDSPInstruction opc)
|
||||
{
|
||||
ERROR_LOG(DSPLLE, "LLE: Unrecognized opcode 0x%04x, pc 0x%04x", opc, g_dsp.pc);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
609
Source/Core/Core/Src/DSP/DspIntMultiplier.cpp
Normal file
609
Source/Core/Core/Src/DSP/DspIntMultiplier.cpp
Normal file
@@ -0,0 +1,609 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
|
||||
// Multiplier and product register control
|
||||
|
||||
#include "DSPInterpreter.h"
|
||||
|
||||
#include "DSPIntCCUtil.h"
|
||||
#include "DSPIntUtil.h"
|
||||
|
||||
namespace DSPInterpreter {
|
||||
|
||||
// Only MULX family instructions have unsigned/mixed support.
|
||||
inline s64 dsp_get_multiply_prod(u16 a, u16 b, u8 sign)
|
||||
{
|
||||
s64 prod;
|
||||
|
||||
if ((sign == 1) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) //unsigned
|
||||
prod = (u32)(a * b);
|
||||
else if ((sign == 2) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) //mixed
|
||||
prod = a * (s16)b;
|
||||
else
|
||||
prod = (s16)a * (s16)b; //signed
|
||||
|
||||
// Conditionally multiply by 2.
|
||||
if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
|
||||
prod <<= 1;
|
||||
|
||||
return prod;
|
||||
}
|
||||
|
||||
inline s64 dsp_multiply(u16 a, u16 b, u8 sign = 0)
|
||||
{
|
||||
s64 prod = dsp_get_multiply_prod(a, b, sign);
|
||||
return prod;
|
||||
}
|
||||
|
||||
inline s64 dsp_multiply_add(u16 a, u16 b, u8 sign = 0)
|
||||
{
|
||||
s64 prod = dsp_get_long_prod() + dsp_get_multiply_prod(a, b, sign);
|
||||
return prod;
|
||||
}
|
||||
|
||||
inline s64 dsp_multiply_sub(u16 a, u16 b, u8 sign = 0)
|
||||
{
|
||||
s64 prod = dsp_get_long_prod() - dsp_get_multiply_prod(a, b, sign);
|
||||
return prod;
|
||||
}
|
||||
|
||||
inline s64 dsp_multiply_mulx(u8 axh0, u8 axh1, u16 val1, u16 val2)
|
||||
{
|
||||
s64 result;
|
||||
|
||||
if ((axh0==0) && (axh1==0))
|
||||
result = dsp_multiply(val1, val2, 1); // unsigned support ON if both ax?.l regs are used
|
||||
else if ((axh0==0) && (axh1==1))
|
||||
result = dsp_multiply(val1, val2, 2); // mixed support ON (u16)axl.0 * (s16)axh.1
|
||||
else if ((axh0==1) && (axh1==0))
|
||||
result = dsp_multiply(val2, val1, 2); // mixed support ON (u16)axl.1 * (s16)axh.0
|
||||
else
|
||||
result = dsp_multiply(val1, val2, 0); // unsigned support OFF if both ax?.h regs are used
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// CLRP
|
||||
// 1000 0100 xxxx xxxx
|
||||
// Clears product register $prod.
|
||||
// Magic numbers taken from duddie's doc
|
||||
//
|
||||
// 00ff_(fff0 + 0010)_0000 = 0100_0000_0000, conveniently, lower 40bits = 0
|
||||
//
|
||||
// It's not ok, to just zero all of them, correct values should be set because of
|
||||
// direct use of prod regs by AX/AXWII (look @that part of ucode).
|
||||
void clrp(const UDSPInstruction opc)
|
||||
{
|
||||
zeroWriteBackLog();
|
||||
|
||||
g_dsp.r.prod.l = 0x0000;
|
||||
g_dsp.r.prod.m = 0xfff0;
|
||||
g_dsp.r.prod.h = 0x00ff;
|
||||
g_dsp.r.prod.m2 = 0x0010;
|
||||
}
|
||||
|
||||
// TSTPROD
|
||||
// 1000 0101 xxxx xxxx
|
||||
// Test prod regs value.
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void tstprod(const UDSPInstruction opc)
|
||||
{
|
||||
s64 prod = dsp_get_long_prod();
|
||||
Update_SR_Register64(prod);
|
||||
zeroWriteBackLog();
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MOVP $acD
|
||||
// 0110 111d xxxx xxxx
|
||||
// Moves multiply product from $prod register to accumulator $acD register.
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void movp(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 8) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_prod();
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_acc(dreg, acc);
|
||||
Update_SR_Register64(acc);
|
||||
}
|
||||
|
||||
// MOVNP $acD
|
||||
// 0111 111d xxxx xxxx
|
||||
// Moves negative of multiply product from $prod register to accumulator
|
||||
// $acD register.
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void movnp(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 8) & 0x1;
|
||||
|
||||
s64 acc = -dsp_get_long_prod();
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_acc(dreg, acc);
|
||||
Update_SR_Register64(acc);
|
||||
}
|
||||
|
||||
// MOVPZ $acD
|
||||
// 1111 111d xxxx xxxx
|
||||
// Moves multiply product from $prod register to accumulator $acD
|
||||
// register and sets (rounds) $acD.l to 0
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void movpz(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 8) & 0x01;
|
||||
|
||||
s64 acc = dsp_get_long_prod_round_prodl();
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_acc(dreg, acc);
|
||||
Update_SR_Register64(acc);
|
||||
}
|
||||
|
||||
// ADDPAXZ $acD, $axS
|
||||
// 1111 10sd xxxx xxxx
|
||||
// Adds secondary accumulator $axS to product register and stores result
|
||||
// in accumulator register. Low 16-bits of $acD ($acD.l) are set (round) to 0.
|
||||
//
|
||||
// TODO: ugly code and still small error here (+/- 1 in .m - randomly)
|
||||
// flags out: --xx xx0x
|
||||
void addpaxz(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
s64 oldprod = dsp_get_long_prod();
|
||||
s64 prod = dsp_get_long_prod_round_prodl();
|
||||
s64 ax = dsp_get_long_acx(sreg);
|
||||
s64 res = prod + (ax & ~0xffff);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_acc(dreg, res);
|
||||
res = dsp_get_long_acc(dreg);
|
||||
Update_SR_Register64(res, isCarry(oldprod, res), false);
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MULAXH
|
||||
// 1000 0011 xxxx xxxx
|
||||
// Multiply $ax0.h by $ax0.h
|
||||
void mulaxh(const UDSPInstruction opc)
|
||||
{
|
||||
s64 prod = dsp_multiply(dsp_get_ax_h(0), dsp_get_ax_h(0));
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MUL $axS.l, $axS.h
|
||||
// 1001 s000 xxxx xxxx
|
||||
// Multiply low part $axS.l of secondary accumulator $axS by high part
|
||||
// $axS.h of secondary accumulator $axS (treat them both as signed).
|
||||
void mul(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 11) & 0x1;
|
||||
|
||||
u16 axl = dsp_get_ax_l(sreg);
|
||||
u16 axh = dsp_get_ax_h(sreg);
|
||||
s64 prod = dsp_multiply(axh, axl);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
// MULAC $axS.l, $axS.h, $acR
|
||||
// 1001 s10r xxxx xxxx
|
||||
// Add product register to accumulator register $acR. Multiply low part
|
||||
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
|
||||
// accumulator $axS (treat them both as signed).
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulac(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 11) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
|
||||
u16 axl = dsp_get_ax_l(sreg);
|
||||
u16 axh = dsp_get_ax_h(sreg);
|
||||
s64 prod = dsp_multiply(axl, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
// MULMV $axS.l, $axS.h, $acR
|
||||
// 1001 s11r xxxx xxxx
|
||||
// Move product register to accumulator register $acR. Multiply low part
|
||||
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
|
||||
// accumulator $axS (treat them both as signed).
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulmv(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 sreg = ((opc >> 11) & 0x1);
|
||||
|
||||
s64 acc = dsp_get_long_prod();
|
||||
u16 axl = dsp_get_ax_l(sreg);
|
||||
u16 axh = dsp_get_ax_h(sreg);
|
||||
s64 prod = dsp_multiply(axl, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
// MULMVZ $axS.l, $axS.h, $acR
|
||||
// 1001 s01r xxxx xxxx
|
||||
// Move product register to accumulator register $acR and clear (round) low part
|
||||
// of accumulator register $acR.l. Multiply low part $axS.l of secondary
|
||||
// accumulator $axS by high part $axS.h of secondary accumulator $axS (treat
|
||||
// them both as signed).
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulmvz(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 11) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_prod_round_prodl();
|
||||
u16 axl = dsp_get_ax_l(sreg);
|
||||
u16 axh = dsp_get_ax_h(sreg);
|
||||
s64 prod = dsp_multiply(axl, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MULX $ax0.S, $ax1.T
|
||||
// 101s t000 xxxx xxxx
|
||||
// Multiply one part $ax0 by one part $ax1.
|
||||
// Part is selected by S and T bits. Zero selects low part, one selects high part.
|
||||
void mulx(const UDSPInstruction opc)
|
||||
{
|
||||
u8 treg = ((opc >> 11) & 0x1);
|
||||
u8 sreg = ((opc >> 12) & 0x1);
|
||||
|
||||
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
// MULXAC $ax0.S, $ax1.T, $acR
|
||||
// 101s t01r xxxx xxxx
|
||||
// Add product register to accumulator register $acR. Multiply one part
|
||||
// $ax0 by one part $ax1. Part is selected by S and
|
||||
// T bits. Zero selects low part, one selects high part.
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulxac(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
|
||||
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
// MULXMV $ax0.S, $ax1.T, $acR
|
||||
// 101s t11r xxxx xxxx
|
||||
// Move product register to accumulator register $acR. Multiply one part
|
||||
// $ax0 by one part $ax1. Part is selected by S and
|
||||
// T bits. Zero selects low part, one selects high part.
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulxmv(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = ((opc >> 8) & 0x1);
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_prod();
|
||||
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
// MULXMVZ $ax0.S, $ax1.T, $acR
|
||||
// 101s t01r xxxx xxxx
|
||||
// Move product register to accumulator register $acR and clear (round) low part
|
||||
// of accumulator register $acR.l. Multiply one part $ax0 by one part $ax1
|
||||
// Part is selected by S and T bits. Zero selects low part,
|
||||
// one selects high part.
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulxmvz(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_prod_round_prodl();
|
||||
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MULC $acS.m, $axT.h
|
||||
// 110s t000 xxxx xxxx
|
||||
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
|
||||
// secondary accumulator $axS (treat them both as signed).
|
||||
void mulc(const UDSPInstruction opc)
|
||||
{
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
u16 accm = dsp_get_acc_m(sreg);
|
||||
u16 axh = dsp_get_ax_h(treg);
|
||||
s64 prod = dsp_multiply(accm, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
// MULCAC $acS.m, $axT.h, $acR
|
||||
// 110s t10r xxxx xxxx
|
||||
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
|
||||
// secondary accumulator $axS (treat them both as signed). Add product
|
||||
// register before multiplication to accumulator $acR.
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulcac(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
|
||||
u16 accm = dsp_get_acc_m(sreg);
|
||||
u16 axh = dsp_get_ax_h(treg);
|
||||
s64 prod = dsp_multiply(accm, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
// MULCMV $acS.m, $axT.h, $acR
|
||||
// 110s t11r xxxx xxxx
|
||||
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
||||
// secondary accumulator $axT (treat them both as signed). Move product
|
||||
// register before multiplication to accumulator $acR.
|
||||
// possible mistake in duddie's doc axT.h rather than axS.h
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulcmv(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_prod();
|
||||
u16 accm = dsp_get_acc_m(sreg);
|
||||
u16 axh = dsp_get_ax_h(treg);
|
||||
s64 prod = dsp_multiply(accm, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
// MULCMVZ $acS.m, $axT.h, $acR
|
||||
// 110s t01r xxxx xxxx
|
||||
// (fixed possible bug in duddie's description, s->t)
|
||||
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
||||
// secondary accumulator $axT (treat them both as signed). Move product
|
||||
// register before multiplication to accumulator $acR, set (round) low part of
|
||||
// accumulator $acR.l to zero.
|
||||
//
|
||||
// flags out: --xx xx0x
|
||||
void mulcmvz(const UDSPInstruction opc)
|
||||
{
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
s64 acc = dsp_get_long_prod_round_prodl();
|
||||
u16 accm = dsp_get_acc_m(sreg);
|
||||
u16 axh = dsp_get_ax_h(treg);
|
||||
s64 prod = dsp_multiply(accm, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
dsp_set_long_acc(rreg, acc);
|
||||
Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MADDX ax0.S ax1.T
|
||||
// 1110 00st xxxx xxxx
|
||||
// Multiply one part of secondary accumulator $ax0 (selected by S) by
|
||||
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
|
||||
// signed) and add result to product register.
|
||||
void maddx(const UDSPInstruction opc)
|
||||
{
|
||||
u8 treg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
s64 prod = dsp_multiply_add(val1, val2);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
// MSUBX $(0x18+S*2), $(0x19+T*2)
|
||||
// 1110 01st xxxx xxxx
|
||||
// Multiply one part of secondary accumulator $ax0 (selected by S) by
|
||||
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
|
||||
// signed) and subtract result from product register.
|
||||
void msubx(const UDSPInstruction opc)
|
||||
{
|
||||
u8 treg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
s64 prod = dsp_multiply_sub(val1, val2);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
// MADDC $acS.m, $axT.h
|
||||
// 1110 10st xxxx xxxx
|
||||
// Multiply middle part of accumulator $acS.m by high part of secondary
|
||||
// accumulator $axT.h (treat them both as signed) and add result to product
|
||||
// register.
|
||||
void maddc(const UDSPInstruction opc)
|
||||
{
|
||||
u8 treg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
u16 accm = dsp_get_acc_m(sreg);
|
||||
u16 axh = dsp_get_ax_h(treg);
|
||||
s64 prod = dsp_multiply_add(accm, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
// MSUBC $acS.m, $axT.h
|
||||
// 1110 11st xxxx xxxx
|
||||
// Multiply middle part of accumulator $acS.m by high part of secondary
|
||||
// accumulator $axT.h (treat them both as signed) and subtract result from
|
||||
// product register.
|
||||
void msubc(const UDSPInstruction opc)
|
||||
{
|
||||
u8 treg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
u16 accm = dsp_get_acc_m(sreg);
|
||||
u16 axh = dsp_get_ax_h(treg);
|
||||
s64 prod = dsp_multiply_sub(accm, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
// MADD $axS.l, $axS.h
|
||||
// 1111 001s xxxx xxxx
|
||||
// Multiply low part $axS.l of secondary accumulator $axS by high part
|
||||
// $axS.h of secondary accumulator $axS (treat them both as signed) and add
|
||||
// result to product register.
|
||||
void madd(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 8) & 0x1;
|
||||
|
||||
u16 axl = dsp_get_ax_l(sreg);
|
||||
u16 axh = dsp_get_ax_h(sreg);
|
||||
s64 prod = dsp_multiply_add(axl, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
// MSUB $axS.l, $axS.h
|
||||
// 1111 011s xxxx xxxx
|
||||
// Multiply low part $axS.l of secondary accumulator $axS by high part
|
||||
// $axS.h of secondary accumulator $axS (treat them both as signed) and
|
||||
// subtract result from product register.
|
||||
void msub(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 8) & 0x1;
|
||||
|
||||
u16 axl = dsp_get_ax_l(sreg);
|
||||
u16 axh = dsp_get_ax_h(sreg);
|
||||
s64 prod = dsp_multiply_sub(axl, axh);
|
||||
|
||||
zeroWriteBackLog();
|
||||
|
||||
dsp_set_long_prod(prod);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
1862
Source/Core/Core/Src/DSP/Jit/DSPJitArithmetic.cpp
Normal file
1862
Source/Core/Core/Src/DSP/Jit/DSPJitArithmetic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
606
Source/Core/Core/Src/DSP/Jit/DSPJitBranch.cpp
Normal file
606
Source/Core/Core/Src/DSP/Jit/DSPJitBranch.cpp
Normal file
@@ -0,0 +1,606 @@
|
||||
// Copyright (C) 2010 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../DSPMemoryMap.h"
|
||||
#include "../DSPEmitter.h"
|
||||
#include "../DSPStacks.h"
|
||||
#include "../DSPAnalyzer.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
template <void(*jitCode)(const UDSPInstruction, DSPEmitter&)>
|
||||
static void ReJitConditional(const UDSPInstruction opc, DSPEmitter& emitter)
|
||||
{
|
||||
u8 cond = opc & 0xf;
|
||||
if (cond == 0xf) {// Always true.
|
||||
jitCode(opc,emitter);
|
||||
return;
|
||||
}
|
||||
FixupBranch skipCode2;
|
||||
emitter.dsp_op_read_reg(DSP_REG_SR, RAX);
|
||||
DSPJitRegCache c2(emitter.gpr);
|
||||
switch(cond)
|
||||
{
|
||||
case 0x0: // GE - Greater Equal
|
||||
case 0x1: // L - Less
|
||||
case 0x2: // G - Greater
|
||||
case 0x3: // LE - Less Equal
|
||||
emitter.MOV(16, R(EDX), R(EAX));
|
||||
emitter.SHR(16, R(EDX), Imm8(3)); //SR_SIGN flag
|
||||
emitter.NOT(16, R(EDX));
|
||||
emitter.SHR(16, R(EAX), Imm8(1)); //SR_OVERFLOW flag
|
||||
emitter.NOT(16, R(EAX));
|
||||
emitter.XOR(16, R(EAX), R(EDX));
|
||||
if (cond < 0x2) {
|
||||
emitter.TEST(16, R(EAX), Imm16(1));
|
||||
break;
|
||||
}
|
||||
c2 = emitter.gpr;
|
||||
emitter.TEST(16, R(EAX), Imm16(1));
|
||||
|
||||
//LE: problem in here, half the tests fail
|
||||
skipCode2 = emitter.J_CC(CC_NE);
|
||||
//skipCode2 = emitter.J_CC((CCFlags)(CC_NE - (cond & 1)));
|
||||
emitter.dsp_op_read_reg(DSP_REG_SR, RAX);
|
||||
emitter.TEST(16, R(EAX), Imm16(SR_ARITH_ZERO));
|
||||
break;
|
||||
case 0x4: // NZ - Not Zero
|
||||
case 0x5: // Z - Zero
|
||||
emitter.TEST(16, R(EAX), Imm16(SR_ARITH_ZERO));
|
||||
break;
|
||||
case 0x6: // NC - Not carry
|
||||
case 0x7: // C - Carry
|
||||
emitter.TEST(16, R(EAX), Imm16(SR_CARRY));
|
||||
break;
|
||||
case 0x8: // ? - Not over s32
|
||||
case 0x9: // ? - Over s32
|
||||
emitter.TEST(16, R(EAX), Imm16(SR_OVER_S32));
|
||||
break;
|
||||
case 0xa: // ?
|
||||
case 0xb: // ?
|
||||
{
|
||||
//full of fail, both
|
||||
emitter.TEST(16, R(EAX), Imm16(SR_OVER_S32 | SR_TOP2BITS));
|
||||
FixupBranch skipArithZero = emitter.J_CC(CC_E);
|
||||
emitter.TEST(16, R(EAX), Imm16(SR_ARITH_ZERO));
|
||||
FixupBranch setZero = emitter.J_CC(CC_NE);
|
||||
|
||||
emitter.MOV(16, R(EAX), Imm16(1));
|
||||
FixupBranch toEnd = emitter.J();
|
||||
|
||||
emitter.SetJumpTarget(skipArithZero);
|
||||
emitter.SetJumpTarget(setZero);
|
||||
emitter.XOR(16, R(EAX), R(EAX));
|
||||
emitter.SetJumpTarget(toEnd);
|
||||
emitter.SETcc(CC_E, R(EAX));
|
||||
emitter.TEST(8, R(EAX), R(EAX));
|
||||
break;
|
||||
//c2 = emitter.gpr;
|
||||
//emitter.TEST(16, R(EAX), Imm16(SR_OVER_S32 | SR_TOP2BITS));
|
||||
//skipCode2 = emitter.J_CC((CCFlags)(CC_E + (cond & 1)));
|
||||
//emitter.TEST(16, R(EAX), Imm16(SR_ARITH_ZERO));
|
||||
//break;
|
||||
}
|
||||
case 0xc: // LNZ - Logic Not Zero
|
||||
case 0xd: // LZ - Logic Zero
|
||||
emitter.TEST(16, R(EAX), Imm16(SR_LOGIC_ZERO));
|
||||
break;
|
||||
case 0xe: // 0 - Overflow
|
||||
emitter.TEST(16, R(EAX), Imm16(SR_OVERFLOW));
|
||||
break;
|
||||
}
|
||||
DSPJitRegCache c1(emitter.gpr);
|
||||
FixupBranch skipCode = cond == 0xe ? emitter.J_CC(CC_E) : emitter.J_CC((CCFlags)(CC_NE - (cond & 1)));
|
||||
jitCode(opc,emitter);
|
||||
emitter.gpr.flushRegs(c1);
|
||||
emitter.SetJumpTarget(skipCode);
|
||||
if ((cond | 1) == 0x3) {// || (cond | 1) == 0xb)
|
||||
emitter.gpr.flushRegs(c2);
|
||||
emitter.SetJumpTarget(skipCode2);
|
||||
} else {
|
||||
c2.drop();
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteBranchExit(DSPEmitter& emitter)
|
||||
{
|
||||
emitter.SaveDSPRegs();
|
||||
if (DSPAnalyzer::code_flags[emitter.startAddr] & DSPAnalyzer::CODE_IDLE_SKIP)
|
||||
{
|
||||
emitter.MOV(16, R(EAX), Imm16(0x1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
emitter.MOV(16, R(EAX), Imm16(emitter.blockSize[emitter.startAddr]));
|
||||
}
|
||||
emitter.JMP(emitter.returnDispatcher, true);
|
||||
}
|
||||
|
||||
static void WriteBlockLink(DSPEmitter& emitter, u16 dest)
|
||||
{
|
||||
// Jump directly to the called block if it has already been compiled.
|
||||
if (!(dest >= emitter.startAddr && dest <= emitter.compilePC))
|
||||
{
|
||||
if (emitter.blockLinks[dest] != 0 )
|
||||
{
|
||||
emitter.gpr.flushRegs();
|
||||
#ifdef _M_IX86 // All32
|
||||
// Check if we have enough cycles to execute the next block
|
||||
emitter.MOV(16, R(ESI), M(&cyclesLeft));
|
||||
emitter.CMP(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
|
||||
FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
|
||||
|
||||
emitter.SUB(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr]));
|
||||
emitter.MOV(16, M(&cyclesLeft), R(ESI));
|
||||
#else
|
||||
// Check if we have enough cycles to execute the next block
|
||||
emitter.MOV(64, R(R12), ImmPtr(&cyclesLeft));
|
||||
emitter.CMP(16, MatR(R12), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
|
||||
FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
|
||||
|
||||
emitter.SUB(16, MatR(R12), Imm16(emitter.blockSize[emitter.startAddr]));
|
||||
#endif
|
||||
emitter.JMP(emitter.blockLinks[dest], true);
|
||||
emitter.SetJumpTarget(notEnoughCycles);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The destination has not been compiled yet. Add it to the list
|
||||
// of blocks that this block is waiting on.
|
||||
emitter.unresolvedJumps[emitter.startAddr].push_back(dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void r_jcc(const UDSPInstruction opc, DSPEmitter& emitter)
|
||||
{
|
||||
u16 dest = dsp_imem_read(emitter.compilePC + 1);
|
||||
const DSPOPCTemplate *opcode = GetOpTemplate(opc);
|
||||
|
||||
// If the block is unconditional, attempt to link block
|
||||
if (opcode->uncond_branch)
|
||||
WriteBlockLink(emitter, dest);
|
||||
#ifdef _M_IX86 // All32
|
||||
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
|
||||
#else
|
||||
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
emitter.MOV(16, MatR(RAX), Imm16(dest));
|
||||
#endif
|
||||
WriteBranchExit(emitter);
|
||||
}
|
||||
// Generic jmp implementation
|
||||
// Jcc addressA
|
||||
// 0000 0010 1001 cccc
|
||||
// aaaa aaaa aaaa aaaa
|
||||
// Jump to addressA if condition cc has been met. Set program counter to
|
||||
// address represented by value that follows this "jmp" instruction.
|
||||
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
|
||||
void DSPEmitter::jcc(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 2));
|
||||
#endif
|
||||
ReJitConditional<r_jcc>(opc, *this);
|
||||
}
|
||||
|
||||
void r_jmprcc(const UDSPInstruction opc, DSPEmitter& emitter)
|
||||
{
|
||||
u8 reg = (opc >> 5) & 0x7;
|
||||
//reg can only be DSP_REG_ARx and DSP_REG_IXx now,
|
||||
//no need to handle DSP_REG_STx.
|
||||
emitter.dsp_op_read_reg(reg, RAX, NONE);
|
||||
#ifdef _M_IX86 // All32
|
||||
emitter.MOV(16, M(&g_dsp.pc), R(EAX));
|
||||
#else
|
||||
emitter.MOV(64, R(RSI), ImmPtr(&(g_dsp.pc)));
|
||||
emitter.MOV(16, MatR(RSI), R(RAX));
|
||||
#endif
|
||||
WriteBranchExit(emitter);
|
||||
}
|
||||
// Generic jmpr implementation
|
||||
// JMPcc $R
|
||||
// 0001 0111 rrr0 cccc
|
||||
// Jump to address; set program counter to a value from register $R.
|
||||
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
|
||||
void DSPEmitter::jmprcc(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.pc), Imm16(compilePC + 1));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 1));
|
||||
#endif
|
||||
ReJitConditional<r_jmprcc>(opc, *this);
|
||||
}
|
||||
|
||||
void r_call(const UDSPInstruction opc, DSPEmitter& emitter)
|
||||
{
|
||||
emitter.MOV(16, R(DX), Imm16(emitter.compilePC + 2));
|
||||
emitter.dsp_reg_store_stack(DSP_STACK_C);
|
||||
u16 dest = dsp_imem_read(emitter.compilePC + 1);
|
||||
const DSPOPCTemplate *opcode = GetOpTemplate(opc);
|
||||
|
||||
// If the block is unconditional, attempt to link block
|
||||
if (opcode->uncond_branch)
|
||||
WriteBlockLink(emitter, dest);
|
||||
#ifdef _M_IX86 // All32
|
||||
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
|
||||
#else
|
||||
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
emitter.MOV(16, MatR(RAX), Imm16(dest));
|
||||
#endif
|
||||
WriteBranchExit(emitter);
|
||||
}
|
||||
// Generic call implementation
|
||||
// CALLcc addressA
|
||||
// 0000 0010 1011 cccc
|
||||
// aaaa aaaa aaaa aaaa
|
||||
// Call function if condition cc has been met. Push program counter of
|
||||
// instruction following "call" to $st0. Set program counter to address
|
||||
// represented by value that follows this "call" instruction.
|
||||
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
|
||||
void DSPEmitter::call(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 2));
|
||||
#endif
|
||||
ReJitConditional<r_call>(opc, *this);
|
||||
}
|
||||
|
||||
void r_callr(const UDSPInstruction opc, DSPEmitter& emitter)
|
||||
{
|
||||
u8 reg = (opc >> 5) & 0x7;
|
||||
emitter.MOV(16, R(DX), Imm16(emitter.compilePC + 1));
|
||||
emitter.dsp_reg_store_stack(DSP_STACK_C);
|
||||
emitter.dsp_op_read_reg(reg, RAX, NONE);
|
||||
#ifdef _M_IX86 // All32
|
||||
emitter.MOV(16, M(&g_dsp.pc), R(EAX));
|
||||
#else
|
||||
emitter.MOV(64, R(RSI), ImmPtr(&(g_dsp.pc)));
|
||||
emitter.MOV(16, MatR(RSI), R(RAX));
|
||||
#endif
|
||||
WriteBranchExit(emitter);
|
||||
}
|
||||
// Generic callr implementation
|
||||
// CALLRcc $R
|
||||
// 0001 0111 rrr1 cccc
|
||||
// Call function if condition cc has been met. Push program counter of
|
||||
// instruction following "call" to call stack $st0. Set program counter to
|
||||
// register $R.
|
||||
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
|
||||
void DSPEmitter::callr(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.pc), Imm16(compilePC + 1));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 1));
|
||||
#endif
|
||||
ReJitConditional<r_callr>(opc, *this);
|
||||
}
|
||||
|
||||
void r_ifcc(const UDSPInstruction opc, DSPEmitter& emitter)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
emitter.MOV(16, M(&g_dsp.pc), Imm16(emitter.compilePC + 1));
|
||||
#else
|
||||
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
emitter.MOV(16, MatR(RAX), Imm16(emitter.compilePC + 1));
|
||||
#endif
|
||||
}
|
||||
// Generic if implementation
|
||||
// IFcc
|
||||
// 0000 0010 0111 cccc
|
||||
// Execute following opcode if the condition has been met.
|
||||
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
|
||||
void DSPEmitter::ifcc(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.pc), Imm16((compilePC + 1) + opTable[compilePC + 1]->size));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16((compilePC + 1) + opTable[compilePC + 1]->size));
|
||||
#endif
|
||||
ReJitConditional<r_ifcc>(opc, *this);
|
||||
WriteBranchExit(*this);
|
||||
}
|
||||
|
||||
void r_ret(const UDSPInstruction opc, DSPEmitter& emitter)
|
||||
{
|
||||
emitter.dsp_reg_load_stack(DSP_STACK_C);
|
||||
#ifdef _M_IX86 // All32
|
||||
emitter.MOV(16, M(&g_dsp.pc), R(DX));
|
||||
#else
|
||||
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
emitter.MOV(16, MatR(RAX), R(DX));
|
||||
#endif
|
||||
WriteBranchExit(emitter);
|
||||
}
|
||||
|
||||
// Generic ret implementation
|
||||
// RETcc
|
||||
// 0000 0010 1101 cccc
|
||||
// Return from subroutine if condition cc has been met. Pops stored PC
|
||||
// from call stack $st0 and sets $pc to this location.
|
||||
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
|
||||
void DSPEmitter::ret(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.pc), Imm16(compilePC + 1));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 1));
|
||||
#endif
|
||||
ReJitConditional<r_ret>(opc, *this);
|
||||
}
|
||||
|
||||
// RTI
|
||||
// 0000 0010 1111 1111
|
||||
// Return from exception. Pops stored status register $sr from data stack
|
||||
// $st1 and program counter PC from call stack $st0 and sets $pc to this
|
||||
// location.
|
||||
void DSPEmitter::rti(const UDSPInstruction opc)
|
||||
{
|
||||
// g_dsp.r[DSP_REG_SR] = dsp_reg_load_stack(DSP_STACK_D);
|
||||
dsp_reg_load_stack(DSP_STACK_D);
|
||||
dsp_op_write_reg(DSP_REG_SR, RDX);
|
||||
// g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
|
||||
dsp_reg_load_stack(DSP_STACK_C);
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.pc), R(DX));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), R(DX));
|
||||
#endif
|
||||
}
|
||||
|
||||
// HALT
|
||||
// 0000 0000 0020 0001
|
||||
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
|
||||
void DSPEmitter::halt(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
OR(16, M(&g_dsp.cr), Imm16(4));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&g_dsp.cr));
|
||||
OR(16, MatR(RAX), Imm16(4));
|
||||
#endif
|
||||
// g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
|
||||
dsp_reg_load_stack(DSP_STACK_C);
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.pc), R(DX));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), R(DX));
|
||||
#endif
|
||||
}
|
||||
|
||||
// LOOP handling: Loop stack is used to control execution of repeated blocks of
|
||||
// instructions. Whenever there is value on stack $st2 and current PC is equal
|
||||
// value at $st2, then value at stack $st3 is decremented. If value is not zero
|
||||
// then PC is modified with value from call stack $st0. Otherwise values from
|
||||
// call stack $st0 and both loop stacks $st2 and $st3 are popped and execution
|
||||
// continues at next opcode.
|
||||
void DSPEmitter::HandleLoop()
|
||||
{
|
||||
#ifdef _M_IX86 // All32
|
||||
MOVZX(32, 16, EAX, M(&g_dsp.r.st[2]));
|
||||
MOVZX(32, 16, ECX, M(&g_dsp.r.st[3]));
|
||||
#else
|
||||
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
MOVZX(32, 16, EAX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[2])));
|
||||
MOVZX(32, 16, ECX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[3])));
|
||||
#endif
|
||||
|
||||
CMP(32, R(RCX), Imm32(0));
|
||||
FixupBranch rLoopCntG = J_CC(CC_LE, true);
|
||||
CMP(16, R(RAX), Imm16(compilePC - 1));
|
||||
FixupBranch rLoopAddrG = J_CC(CC_NE, true);
|
||||
|
||||
#ifdef _M_IX86 // All32
|
||||
SUB(16, M(&(g_dsp.r.st[3])), Imm16(1));
|
||||
CMP(16, M(&(g_dsp.r.st[3])), Imm16(0));
|
||||
#else
|
||||
SUB(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[3])), Imm16(1));
|
||||
CMP(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[3])), Imm16(0));
|
||||
#endif
|
||||
|
||||
FixupBranch loadStack = J_CC(CC_LE, true);
|
||||
#ifdef _M_IX86 // All32
|
||||
MOVZX(32, 16, ECX, M(&(g_dsp.r.st[0])));
|
||||
MOV(16, M(&g_dsp.pc), R(RCX));
|
||||
#else
|
||||
MOVZX(32, 16, RCX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[0])));
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), R(RCX));
|
||||
#endif
|
||||
FixupBranch loopUpdated = J(true);
|
||||
|
||||
SetJumpTarget(loadStack);
|
||||
dsp_reg_load_stack(0);
|
||||
dsp_reg_load_stack(2);
|
||||
dsp_reg_load_stack(3);
|
||||
|
||||
SetJumpTarget(loopUpdated);
|
||||
SetJumpTarget(rLoopAddrG);
|
||||
SetJumpTarget(rLoopCntG);
|
||||
|
||||
}
|
||||
|
||||
// LOOP $R
|
||||
// 0000 0000 010r rrrr
|
||||
// Repeatedly execute following opcode until counter specified by value
|
||||
// from register $R reaches zero. Each execution decrement counter. Register
|
||||
// $R remains unchanged. If register $R is set to zero at the beginning of loop
|
||||
// then looped instruction will not get executed.
|
||||
// Actually, this instruction simply prepares the loop stacks for the above.
|
||||
// The looping hardware takes care of the rest.
|
||||
void DSPEmitter::loop(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x1f;
|
||||
// u16 cnt = g_dsp.r[reg];
|
||||
dsp_op_read_reg(reg, RDX, ZERO);
|
||||
u16 loop_pc = compilePC + 1;
|
||||
|
||||
CMP(16, R(EDX), Imm16(0));
|
||||
FixupBranch cnt = J_CC(CC_Z, true);
|
||||
dsp_reg_store_stack(3);
|
||||
MOV(16, R(RDX), Imm16(compilePC + 1));
|
||||
dsp_reg_store_stack(0);
|
||||
MOV(16, R(RDX), Imm16(loop_pc));
|
||||
dsp_reg_store_stack(2);
|
||||
|
||||
SetJumpTarget(cnt);
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 1));
|
||||
#endif
|
||||
}
|
||||
|
||||
// LOOPI #I
|
||||
// 0001 0000 iiii iiii
|
||||
// Repeatedly execute following opcode until counter specified by
|
||||
// immediate value I reaches zero. Each execution decrement counter. If
|
||||
// immediate value I is set to zero at the beginning of loop then looped
|
||||
// instruction will not get executed.
|
||||
// Actually, this instruction simply prepares the loop stacks for the above.
|
||||
// The looping hardware takes care of the rest.
|
||||
void DSPEmitter::loopi(const UDSPInstruction opc)
|
||||
{
|
||||
u16 cnt = opc & 0xff;
|
||||
u16 loop_pc = compilePC + 1;
|
||||
|
||||
if (cnt)
|
||||
{
|
||||
MOV(16, R(RDX), Imm16(compilePC + 1));
|
||||
dsp_reg_store_stack(0);
|
||||
MOV(16, R(RDX), Imm16(loop_pc));
|
||||
dsp_reg_store_stack(2);
|
||||
MOV(16, R(RDX), Imm16(cnt));
|
||||
dsp_reg_store_stack(3);
|
||||
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 1));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BLOOP $R, addrA
|
||||
// 0000 0000 011r rrrr
|
||||
// aaaa aaaa aaaa aaaa
|
||||
// Repeatedly execute block of code starting at following opcode until
|
||||
// counter specified by value from register $R reaches zero. Block ends at
|
||||
// specified address addrA inclusive, ie. opcode at addrA is the last opcode
|
||||
// included in loop. Counter is pushed on loop stack $st3, end of block address
|
||||
// is pushed on loop stack $st2 and repeat address is pushed on call stack $st0.
|
||||
// Up to 4 nested loops are allowed.
|
||||
void DSPEmitter::bloop(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x1f;
|
||||
// u16 cnt = g_dsp.r[reg];
|
||||
dsp_op_read_reg(reg, RDX, ZERO);
|
||||
u16 loop_pc = dsp_imem_read(compilePC + 1);
|
||||
|
||||
CMP(16, R(EDX), Imm16(0));
|
||||
FixupBranch cnt = J_CC(CC_Z, true);
|
||||
dsp_reg_store_stack(3);
|
||||
MOV(16, R(RDX), Imm16(compilePC + 2));
|
||||
dsp_reg_store_stack(0);
|
||||
MOV(16, R(RDX), Imm16(loop_pc));
|
||||
dsp_reg_store_stack(2);
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 2));
|
||||
#endif
|
||||
FixupBranch exit = J();
|
||||
|
||||
SetJumpTarget(cnt);
|
||||
// g_dsp.pc = loop_pc;
|
||||
// dsp_skip_inst();
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.pc), Imm16(loop_pc + opTable[loop_pc]->size));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(loop_pc + opTable[loop_pc]->size));
|
||||
#endif
|
||||
DSPJitRegCache c(gpr);
|
||||
WriteBranchExit(*this);
|
||||
gpr.flushRegs(c,false);
|
||||
SetJumpTarget(exit);
|
||||
}
|
||||
|
||||
// BLOOPI #I, addrA
|
||||
// 0001 0001 iiii iiii
|
||||
// aaaa aaaa aaaa aaaa
|
||||
// Repeatedly execute block of code starting at following opcode until
|
||||
// counter specified by immediate value I reaches zero. Block ends at specified
|
||||
// address addrA inclusive, ie. opcode at addrA is the last opcode included in
|
||||
// loop. Counter is pushed on loop stack $st3, end of block address is pushed
|
||||
// on loop stack $st2 and repeat address is pushed on call stack $st0. Up to 4
|
||||
// nested loops are allowed.
|
||||
void DSPEmitter::bloopi(const UDSPInstruction opc)
|
||||
{
|
||||
u16 cnt = opc & 0xff;
|
||||
// u16 loop_pc = dsp_fetch_code();
|
||||
u16 loop_pc = dsp_imem_read(compilePC + 1);
|
||||
|
||||
if (cnt)
|
||||
{
|
||||
MOV(16, R(RDX), Imm16(compilePC + 2));
|
||||
dsp_reg_store_stack(0);
|
||||
MOV(16, R(RDX), Imm16(loop_pc));
|
||||
dsp_reg_store_stack(2);
|
||||
MOV(16, R(RDX), Imm16(cnt));
|
||||
dsp_reg_store_stack(3);
|
||||
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(compilePC + 2));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// g_dsp.pc = loop_pc;
|
||||
// dsp_skip_inst();
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.pc), Imm16(loop_pc + opTable[loop_pc]->size));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
MOV(16, MatR(RAX), Imm16(loop_pc + opTable[loop_pc]->size));
|
||||
#endif
|
||||
WriteBranchExit(*this);
|
||||
}
|
||||
}
|
||||
323
Source/Core/Core/Src/DSP/Jit/DSPJitCCUtil.cpp
Normal file
323
Source/Core/Core/Src/DSP/Jit/DSPJitCCUtil.cpp
Normal file
@@ -0,0 +1,323 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
|
||||
#include "../DSPIntUtil.h"
|
||||
#include "../DSPEmitter.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
// In: RCX: 1 = carry, 2 = overflow
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::Update_SR_Register(Gen::X64Reg val)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
// // 0x04
|
||||
// if (_Value == 0) g_dsp.r[DSP_REG_SR] |= SR_ARITH_ZERO;
|
||||
CMP(64, R(val), Imm8(0));
|
||||
FixupBranch notZero = J_CC(CC_NZ);
|
||||
OR(16, sr_reg, Imm16(SR_ARITH_ZERO));
|
||||
SetJumpTarget(notZero);
|
||||
|
||||
// // 0x08
|
||||
// if (_Value < 0) g_dsp.r[DSP_REG_SR] |= SR_SIGN;
|
||||
CMP(64, R(val), Imm8(0));
|
||||
FixupBranch greaterThanEqual = J_CC(CC_GE);
|
||||
OR(16, sr_reg, Imm16(SR_SIGN));
|
||||
SetJumpTarget(greaterThanEqual);
|
||||
|
||||
// // 0x10
|
||||
// if (_Value != (s32)_Value) g_dsp.r[DSP_REG_SR] |= SR_OVER_S32;
|
||||
MOVSX(64, 32, RDX, R(val));
|
||||
CMP(64, R(RDX), R(val));
|
||||
FixupBranch noOverS32 = J_CC(CC_E);
|
||||
OR(16, sr_reg, Imm16(SR_OVER_S32));
|
||||
SetJumpTarget(noOverS32);
|
||||
|
||||
// // 0x20 - Checks if top bits of m are equal
|
||||
// if (((_Value & 0xc0000000) == 0) || ((_Value & 0xc0000000) == 0xc0000000))
|
||||
AND(32, R(val), Imm32(0xc0000000));
|
||||
CMP(32, R(val), Imm32(0));
|
||||
FixupBranch zeroC = J_CC(CC_E);
|
||||
CMP(32, R(val), Imm32(0xc0000000));
|
||||
FixupBranch cC = J_CC(CC_NE);
|
||||
SetJumpTarget(zeroC);
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_TOP2BITS;
|
||||
OR(16, sr_reg, Imm16(SR_TOP2BITS));
|
||||
SetJumpTarget(cC);
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
#endif
|
||||
}
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
// In: RCX: 1 = carry, 2 = overflow
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::Update_SR_Register64(Gen::X64Reg val)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
// g_dsp.r[DSP_REG_SR] &= ~SR_CMP_MASK;
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
Update_SR_Register(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
// In: RCX: 1 = carry, 2 = overflow
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::Update_SR_Register64_Carry(Gen::X64Reg val)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
// g_dsp.r[DSP_REG_SR] &= ~SR_CMP_MASK;
|
||||
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
|
||||
|
||||
CMP(64, R(RCX), R(val));
|
||||
|
||||
// 0x01
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_CARRY;
|
||||
// Carry = (acc>res)
|
||||
FixupBranch noCarry = J_CC(CC_BE);
|
||||
OR(16, sr_reg, Imm16(SR_CARRY));
|
||||
SetJumpTarget(noCarry);
|
||||
|
||||
// 0x02 and 0x80
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW;
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW_STICKY;
|
||||
// Overflow = ((acc ^ res) & (ax ^ res)) < 0
|
||||
XOR(64, R(RCX), R(val));
|
||||
XOR(64, R(RDX), R(val));
|
||||
AND(64, R(RCX), R(RDX));
|
||||
CMP(64, R(RCX), Imm8(0));
|
||||
FixupBranch noOverflow = J_CC(CC_GE);
|
||||
OR(16, sr_reg, Imm16(SR_OVERFLOW | SR_OVERFLOW_STICKY));
|
||||
SetJumpTarget(noOverflow);
|
||||
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
Update_SR_Register(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
// In: RCX: 1 = carry, 2 = overflow
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::Update_SR_Register64_Carry2(Gen::X64Reg val)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
// g_dsp.r[DSP_REG_SR] &= ~SR_CMP_MASK;
|
||||
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
|
||||
|
||||
CMP(64, R(RCX), R(val));
|
||||
|
||||
// 0x01
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_CARRY;
|
||||
// Carry2 = (acc>=res)
|
||||
FixupBranch noCarry2 = J_CC(CC_B);
|
||||
OR(16, sr_reg, Imm16(SR_CARRY));
|
||||
SetJumpTarget(noCarry2);
|
||||
|
||||
// 0x02 and 0x80
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW;
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW_STICKY;
|
||||
// Overflow = ((acc ^ res) & (ax ^ res)) < 0
|
||||
XOR(64, R(RCX), R(val));
|
||||
XOR(64, R(RDX), R(val));
|
||||
AND(64, R(RCX), R(RDX));
|
||||
CMP(64, R(RCX), Imm8(0));
|
||||
FixupBranch noOverflow = J_CC(CC_GE);
|
||||
OR(16, sr_reg, Imm16(SR_OVERFLOW | SR_OVERFLOW_STICKY));
|
||||
SetJumpTarget(noOverflow);
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
|
||||
Update_SR_Register();
|
||||
#endif
|
||||
}
|
||||
|
||||
//void DSPEmitter::Update_SR_Register16(s16 _Value, bool carry, bool overflow, bool overS32)
|
||||
//{
|
||||
|
||||
// // 0x20 - Checks if top bits of m are equal
|
||||
// if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
|
||||
// {
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_TOP2BITS;
|
||||
// }
|
||||
//}
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
// In: RCX: 1 = carry, 2 = overflow
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::Update_SR_Register16(Gen::X64Reg val)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
|
||||
|
||||
// // 0x04
|
||||
// if (_Value == 0) g_dsp.r[DSP_REG_SR] |= SR_ARITH_ZERO;
|
||||
CMP(64, R(val), Imm8(0));
|
||||
FixupBranch notZero = J_CC(CC_NZ);
|
||||
OR(16, sr_reg, Imm16(SR_ARITH_ZERO));
|
||||
SetJumpTarget(notZero);
|
||||
|
||||
// // 0x08
|
||||
// if (_Value < 0) g_dsp.r[DSP_REG_SR] |= SR_SIGN;
|
||||
CMP(64, R(val), Imm8(0));
|
||||
FixupBranch greaterThanEqual = J_CC(CC_GE);
|
||||
OR(16, sr_reg, Imm16(SR_SIGN));
|
||||
SetJumpTarget(greaterThanEqual);
|
||||
|
||||
// // 0x20 - Checks if top bits of m are equal
|
||||
// if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
|
||||
//AND(32, R(val), Imm32(0xc0000000));
|
||||
SHR(16, R(val), Imm8(14));
|
||||
CMP(16, R(val), Imm16(0));
|
||||
FixupBranch nZero = J_CC(CC_NE);
|
||||
OR(16, sr_reg, Imm16(SR_TOP2BITS));
|
||||
FixupBranch cC = J();
|
||||
SetJumpTarget(nZero);
|
||||
CMP(16, R(val), Imm16(3));
|
||||
FixupBranch notThree = J_CC(CC_NE);
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_TOP2BITS;
|
||||
OR(16, sr_reg, Imm16(SR_TOP2BITS));
|
||||
SetJumpTarget(notThree);
|
||||
SetJumpTarget(cC);
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
#endif
|
||||
}
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
// In: RCX: 1 = carry, 2 = overflow
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::Update_SR_Register16_OverS32(Gen::X64Reg val)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
|
||||
|
||||
// // 0x10
|
||||
// if (_Value != (s32)_Value) g_dsp.r[DSP_REG_SR] |= SR_OVER_S32;
|
||||
MOVSX(64, 32, RSI, R(val));
|
||||
CMP(64, R(RSI), R(val));
|
||||
FixupBranch noOverS32 = J_CC(CC_E);
|
||||
OR(16, sr_reg, Imm16(SR_OVER_S32));
|
||||
SetJumpTarget(noOverS32);
|
||||
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
// // 0x20 - Checks if top bits of m are equal
|
||||
// if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
|
||||
//AND(32, R(val), Imm32(0xc0000000));
|
||||
Update_SR_Register16(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
//void DSPEmitter::Update_SR_LZ(bool value) {
|
||||
|
||||
// if (value == true)
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_LOGIC_ZERO;
|
||||
// else
|
||||
// g_dsp.r[DSP_REG_SR] &= ~SR_LOGIC_ZERO;
|
||||
//}
|
||||
|
||||
//inline int GetMultiplyModifier()
|
||||
//{
|
||||
// return (g_dsp.r[DSP_REG_SR] & SR_MUL_MODIFY)?1:2;
|
||||
//}
|
||||
|
||||
//inline bool isCarry() {
|
||||
// return (g_dsp.r[DSP_REG_SR] & SR_CARRY) ? true : false;
|
||||
//}
|
||||
|
||||
//inline bool isOverflow() {
|
||||
// return (g_dsp.r[DSP_REG_SR] & SR_OVERFLOW) ? true : false;
|
||||
//}
|
||||
|
||||
//inline bool isOverS32() {
|
||||
// return (g_dsp.r[DSP_REG_SR] & SR_OVER_S32) ? true : false;
|
||||
//}
|
||||
|
||||
//inline bool isLess() {
|
||||
// return (!(g_dsp.r[DSP_REG_SR] & SR_OVERFLOW) != !(g_dsp.r[DSP_REG_SR] & SR_SIGN));
|
||||
//}
|
||||
|
||||
//inline bool isZero() {
|
||||
// return (g_dsp.r[DSP_REG_SR] & SR_ARITH_ZERO) ? true : false;
|
||||
//}
|
||||
|
||||
//inline bool isLogicZero() {
|
||||
// return (g_dsp.r[DSP_REG_SR] & SR_LOGIC_ZERO) ? true : false;
|
||||
//}
|
||||
|
||||
//inline bool isConditionA() {
|
||||
// return (((g_dsp.r[DSP_REG_SR] & SR_OVER_S32) || (g_dsp.r[DSP_REG_SR] & SR_TOP2BITS)) && !(g_dsp.r[DSP_REG_SR] & SR_ARITH_ZERO)) ? true : false;
|
||||
//}
|
||||
|
||||
//see DSPCore.h for flags
|
||||
//bool CheckCondition(u8 _Condition)
|
||||
//{
|
||||
// switch (_Condition & 0xf)
|
||||
// {
|
||||
// case 0xf: // Always true.
|
||||
// return true;
|
||||
// case 0x0: // GE - Greater Equal
|
||||
// return !isLess();
|
||||
// case 0x1: // L - Less
|
||||
// return isLess();
|
||||
// case 0x2: // G - Greater
|
||||
// return !isLess() && !isZero();
|
||||
// case 0x3: // LE - Less Equal
|
||||
// return isLess() || isZero();
|
||||
// case 0x4: // NZ - Not Zero
|
||||
// return !isZero();
|
||||
// case 0x5: // Z - Zero
|
||||
// return isZero();
|
||||
// case 0x6: // NC - Not carry
|
||||
// return !isCarry();
|
||||
// case 0x7: // C - Carry
|
||||
// return isCarry();
|
||||
// case 0x8: // ? - Not over s32
|
||||
// return !isOverS32();
|
||||
// case 0x9: // ? - Over s32
|
||||
// return isOverS32();
|
||||
// case 0xa: // ?
|
||||
// return isConditionA();
|
||||
// case 0xb: // ?
|
||||
// return !isConditionA();
|
||||
// case 0xc: // LNZ - Logic Not Zero
|
||||
// return !isLogicZero();
|
||||
// case 0xd: // LZ - Logic Zero
|
||||
// return isLogicZero();
|
||||
// case 0xe: // 0 - Overflow
|
||||
// return isOverflow();
|
||||
// default:
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
639
Source/Core/Core/Src/DSP/Jit/DSPJitExtOps.cpp
Normal file
639
Source/Core/Core/Src/DSP/Jit/DSPJitExtOps.cpp
Normal file
@@ -0,0 +1,639 @@
|
||||
// Copyright (C) 2010 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#include "../DSPMemoryMap.h"
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
/* It is safe to directly write to the address registers as they are
|
||||
neither read not written by any extendable opcode. The same is true
|
||||
for memory accesses.
|
||||
It probably even is safe to write to all registers except for
|
||||
SR, ACx.x, AXx.x and PROD, which may be modified by the main op.
|
||||
|
||||
This code uses EBX to keep the values of the registers written by
|
||||
the extended op so the main op can still access the old values.
|
||||
storeIndex and storeIndex2 control where the lower and upper 16bits
|
||||
of EBX are written to. Additionally, the upper 16bits can contain the
|
||||
original SR so we can do sign extension in 40bit mode. There is only
|
||||
the 'ld family of opcodes writing to two registers at the same time,
|
||||
and those always are AXx.x, thus no need to leave space for SR for
|
||||
sign extension.
|
||||
*/
|
||||
|
||||
// DR $arR
|
||||
// xxxx xxxx 0000 01rr
|
||||
// Decrement addressing register $arR.
|
||||
void DSPEmitter::dr(const UDSPInstruction opc) {
|
||||
decrement_addr_reg(opc & 0x3);
|
||||
}
|
||||
|
||||
// IR $arR
|
||||
// xxxx xxxx 0000 10rr
|
||||
// Increment addressing register $arR.
|
||||
void DSPEmitter::ir(const UDSPInstruction opc) {
|
||||
increment_addr_reg(opc & 0x3);
|
||||
}
|
||||
|
||||
// NR $arR
|
||||
// xxxx xxxx 0000 11rr
|
||||
// Add corresponding indexing register $ixR to addressing register $arR.
|
||||
void DSPEmitter::nr(const UDSPInstruction opc) {
|
||||
u8 reg = opc & 0x3;
|
||||
|
||||
increase_addr_reg(reg);
|
||||
}
|
||||
|
||||
// MV $axD.D, $acS.S
|
||||
// xxxx xxxx 0001 ddss
|
||||
// Move value of $acS.S to the $axD.D.
|
||||
void DSPEmitter::mv(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
|
||||
u8 dreg = ((opc >> 2) & 0x3);
|
||||
pushExtValueFromReg(dreg + DSP_REG_AXL0, sreg);
|
||||
}
|
||||
|
||||
// S @$arD, $acS.S
|
||||
// xxxx xxxx 001s s0dd
|
||||
// Store value of $acS.S in the memory pointed by register $arD.
|
||||
// Post increment register $arD.
|
||||
void DSPEmitter::s(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = opc & 0x3;
|
||||
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
|
||||
// u16 addr = g_dsp.r[dest];
|
||||
dsp_op_read_reg(dreg, RAX, ZERO);
|
||||
dsp_op_read_reg(sreg, RCX, ZERO);
|
||||
// u16 val = g_dsp.r[src];
|
||||
dmem_write();
|
||||
increment_addr_reg(dreg);
|
||||
}
|
||||
|
||||
// SN @$arD, $acS.S
|
||||
// xxxx xxxx 001s s1dd
|
||||
// Store value of register $acS.S in the memory pointed by register $arD.
|
||||
// Add indexing register $ixD to register $arD.
|
||||
void DSPEmitter::sn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = opc & 0x3;
|
||||
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
|
||||
dsp_op_read_reg(dreg, RAX, ZERO);
|
||||
dsp_op_read_reg(sreg, RCX, ZERO);
|
||||
dmem_write();
|
||||
increase_addr_reg(dreg);
|
||||
}
|
||||
|
||||
// L $axD.D, @$arS
|
||||
// xxxx xxxx 01dd d0ss
|
||||
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
|
||||
// Post increment register $arS.
|
||||
void DSPEmitter::l(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x3;
|
||||
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0; //AX?.?, AC?.[LM]
|
||||
|
||||
pushExtValueFromMem(dreg, sreg);
|
||||
|
||||
if (dreg >= DSP_REG_ACM0) {
|
||||
//save SR too, so we can decide later.
|
||||
//even if only for one bit, can only
|
||||
//store (up to) two registers in EBX,
|
||||
//so store all of SR
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, R(EAX), M(&g_dsp.r.sr));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&g_dsp.r.sr));
|
||||
MOV(16, R(RAX), MatR(RAX));
|
||||
#endif
|
||||
SHL(32, R(EAX), Imm8(16));
|
||||
OR(32, R(EBX), R(EAX));
|
||||
}
|
||||
|
||||
increment_addr_reg(sreg);
|
||||
}
|
||||
|
||||
// LN $axD.D, @$arS
|
||||
// xxxx xxxx 01dd d0ss
|
||||
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
|
||||
// Add indexing register register $ixS to register $arS.
|
||||
void DSPEmitter::ln(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x3;
|
||||
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
|
||||
|
||||
pushExtValueFromMem(dreg, sreg);
|
||||
|
||||
if (dreg >= DSP_REG_ACM0) {
|
||||
//save SR too, so we can decide later.
|
||||
//even if only for one bit, can only
|
||||
//store (up to) two registers in EBX,
|
||||
//so store all of SR
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, R(EAX), M(&g_dsp.r.sr));
|
||||
#else
|
||||
MOV(64, R(RAX), ImmPtr(&g_dsp.r.sr));
|
||||
MOV(16, R(RAX), MatR(RAX));
|
||||
#endif
|
||||
SHL(32, R(EAX), Imm8(16));
|
||||
OR(32, R(EBX), R(EAX));
|
||||
}
|
||||
|
||||
increase_addr_reg(sreg);
|
||||
}
|
||||
|
||||
// LS $axD.D, $acS.m
|
||||
// xxxx xxxx 10dd 000s
|
||||
// Load register $axD.D with value from memory pointed by register
|
||||
// $ar0. Store value from register $acS.m to memory location pointed by
|
||||
// register $ar3. Increment both $ar0 and $ar3.
|
||||
void DSPEmitter::ls(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
dsp_op_read_reg(DSP_REG_AR3, RAX, ZERO);
|
||||
get_acc_m(sreg, ECX, false);
|
||||
dmem_write();
|
||||
|
||||
pushExtValueFromMem(dreg, DSP_REG_AR0);
|
||||
|
||||
increment_addr_reg(DSP_REG_AR3);
|
||||
increment_addr_reg(DSP_REG_AR0);
|
||||
}
|
||||
|
||||
|
||||
// LSN $axD.D, $acS.m
|
||||
// xxxx xxxx 10dd 010s
|
||||
// Load register $axD.D with value from memory pointed by register
|
||||
// $ar0. Store value from register $acS.m to memory location pointed by
|
||||
// register $ar3. Add corresponding indexing register $ix0 to addressing
|
||||
// register $ar0 and increment $ar3.
|
||||
void DSPEmitter::lsn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
dsp_op_read_reg(DSP_REG_AR3, RAX, ZERO);
|
||||
get_acc_m(sreg, ECX, false);
|
||||
dmem_write();
|
||||
|
||||
pushExtValueFromMem(dreg, DSP_REG_AR0);
|
||||
|
||||
increment_addr_reg(DSP_REG_AR3);
|
||||
increase_addr_reg(DSP_REG_AR0);
|
||||
}
|
||||
|
||||
// LSM $axD.D, $acS.m
|
||||
// xxxx xxxx 10dd 100s
|
||||
// Load register $axD.D with value from memory pointed by register
|
||||
// $ar0. Store value from register $acS.m to memory location pointed by
|
||||
// register $ar3. Add corresponding indexing register $ix3 to addressing
|
||||
// register $ar3 and increment $ar0.
|
||||
void DSPEmitter::lsm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
dsp_op_read_reg(DSP_REG_AR3, RAX, ZERO);
|
||||
get_acc_m(sreg, ECX, false);
|
||||
dmem_write();
|
||||
|
||||
pushExtValueFromMem(dreg, DSP_REG_AR0);
|
||||
|
||||
increase_addr_reg(DSP_REG_AR3);
|
||||
increment_addr_reg(DSP_REG_AR0);
|
||||
}
|
||||
|
||||
// LSMN $axD.D, $acS.m
|
||||
// xxxx xxxx 10dd 110s
|
||||
// Load register $axD.D with value from memory pointed by register
|
||||
// $ar0. Store value from register $acS.m to memory location pointed by
|
||||
// register $ar3. Add corresponding indexing register $ix0 to addressing
|
||||
// register $ar0 and add corresponding indexing register $ix3 to addressing
|
||||
// register $ar3.
|
||||
void DSPEmitter::lsnm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
dsp_op_read_reg(DSP_REG_AR3, RAX, ZERO);
|
||||
get_acc_m(sreg, ECX, false);
|
||||
dmem_write();
|
||||
|
||||
pushExtValueFromMem(dreg, DSP_REG_AR0);
|
||||
|
||||
increase_addr_reg(DSP_REG_AR3);
|
||||
increase_addr_reg(DSP_REG_AR0);
|
||||
}
|
||||
|
||||
// SL $acS.m, $axD.D
|
||||
// xxxx xxxx 10dd 001s
|
||||
// Store value from register $acS.m to memory location pointed by register
|
||||
// $ar0. Load register $axD.D with value from memory pointed by register
|
||||
// $ar3. Increment both $ar0 and $ar3.
|
||||
void DSPEmitter::sl(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
dsp_op_read_reg(DSP_REG_AR0, RAX, ZERO);
|
||||
get_acc_m(sreg, ECX, false);
|
||||
dmem_write();
|
||||
|
||||
pushExtValueFromMem(dreg, DSP_REG_AR3);
|
||||
|
||||
increment_addr_reg(DSP_REG_AR3);
|
||||
increment_addr_reg(DSP_REG_AR0);
|
||||
}
|
||||
|
||||
// SLN $acS.m, $axD.D
|
||||
// xxxx xxxx 10dd 011s
|
||||
// Store value from register $acS.m to memory location pointed by register
|
||||
// $ar0. Load register $axD.D with value from memory pointed by register
|
||||
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
|
||||
// and increment $ar3.
|
||||
void DSPEmitter::sln(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
dsp_op_read_reg(DSP_REG_AR0, RAX, ZERO);
|
||||
get_acc_m(sreg, ECX, false);
|
||||
dmem_write();
|
||||
|
||||
pushExtValueFromMem(dreg, DSP_REG_AR3);
|
||||
|
||||
increment_addr_reg(DSP_REG_AR3);
|
||||
increase_addr_reg(DSP_REG_AR0);
|
||||
}
|
||||
|
||||
// SLM $acS.m, $axD.D
|
||||
// xxxx xxxx 10dd 101s
|
||||
// Store value from register $acS.m to memory location pointed by register
|
||||
// $ar0. Load register $axD.D with value from memory pointed by register
|
||||
// $ar3. Add corresponding indexing register $ix3 to addressing register $ar3
|
||||
// and increment $ar0.
|
||||
void DSPEmitter::slm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
dsp_op_read_reg(DSP_REG_AR0, RAX, ZERO);
|
||||
get_acc_m(sreg, ECX, false);
|
||||
dmem_write();
|
||||
|
||||
pushExtValueFromMem(dreg, DSP_REG_AR3);
|
||||
|
||||
increase_addr_reg(DSP_REG_AR3);
|
||||
increment_addr_reg(DSP_REG_AR0);
|
||||
}
|
||||
|
||||
// SLMN $acS.m, $axD.D
|
||||
// xxxx xxxx 10dd 111s
|
||||
// Store value from register $acS.m to memory location pointed by register
|
||||
// $ar0. Load register $axD.D with value from memory pointed by register
|
||||
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
|
||||
// and add corresponding indexing register $ix3 to addressing register $ar3.
|
||||
void DSPEmitter::slnm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1;
|
||||
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
|
||||
dsp_op_read_reg(DSP_REG_AR0, RAX, ZERO);
|
||||
get_acc_m(sreg, ECX, false);
|
||||
dmem_write();
|
||||
|
||||
pushExtValueFromMem(dreg, DSP_REG_AR3);
|
||||
|
||||
increase_addr_reg(DSP_REG_AR3);
|
||||
increase_addr_reg(DSP_REG_AR0);
|
||||
}
|
||||
|
||||
// LD $ax0.d, $ax1.r, @$arS
|
||||
// xxxx xxxx 11dr 00ss
|
||||
// example for "nx'ld $AX0.L, $AX1.L, @$AR3"
|
||||
// Loads the word pointed by AR0 to AX0.H, then loads the word pointed by AR3
|
||||
// to AX0.L. Increments AR0 and AR3. If AR0 and AR3 point into the same
|
||||
// memory page (upper 6 bits of addr are the same -> games are not doing that!)
|
||||
// then the value pointed by AR0 is loaded to BOTH AX0.H and AX0.L. If AR0
|
||||
// points into an invalid memory page (ie 0x2000), then AX0.H keeps its old
|
||||
// value. (not implemented yet) If AR3 points into an invalid memory page, then
|
||||
// AX0.L gets the same value as AX0.H. (not implemented yet)
|
||||
void DSPEmitter::ld(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x1;
|
||||
u8 rreg = (opc >> 4) & 0x1;
|
||||
u8 sreg = opc & 0x3;
|
||||
|
||||
if (sreg != DSP_REG_AR3) {
|
||||
pushExtValueFromMem((dreg << 1) + DSP_REG_AXL0, sreg);
|
||||
|
||||
// if (IsSameMemArea(g_dsp.r[sreg], g_dsp.r[DSP_REG_AR3])) {
|
||||
dsp_op_read_reg(sreg, RSI, NONE);
|
||||
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
|
||||
SHR(16, R(ESI), Imm8(10));
|
||||
SHR(16, R(EDI), Imm8(10));
|
||||
DSPJitRegCache c(gpr);
|
||||
CMP(16, R(ESI), R(EDI));
|
||||
FixupBranch not_equal = J_CC(CC_NE,true);
|
||||
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, sreg);
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch after = J(true);
|
||||
SetJumpTarget(not_equal); // else
|
||||
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, DSP_REG_AR3);
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(after);
|
||||
|
||||
increment_addr_reg(sreg);
|
||||
|
||||
} else {
|
||||
pushExtValueFromMem(rreg + DSP_REG_AXH0, dreg);
|
||||
|
||||
//if (IsSameMemArea(g_dsp.r[dreg], g_dsp.r[DSP_REG_AR3])) {
|
||||
dsp_op_read_reg(dreg, RSI, NONE);
|
||||
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
|
||||
SHR(16, R(ESI), Imm8(10));
|
||||
SHR(16, R(EDI), Imm8(10));
|
||||
DSPJitRegCache c(gpr);
|
||||
CMP(16, R(ESI), R(EDI));
|
||||
FixupBranch not_equal = J_CC(CC_NE, true);
|
||||
pushExtValueFromMem2(rreg + DSP_REG_AXL0, dreg);
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch after = J(true); // else
|
||||
SetJumpTarget(not_equal);
|
||||
pushExtValueFromMem2(rreg + DSP_REG_AXL0, DSP_REG_AR3);
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(after);
|
||||
|
||||
increment_addr_reg(dreg);
|
||||
}
|
||||
|
||||
increment_addr_reg(DSP_REG_AR3);
|
||||
}
|
||||
|
||||
// LDN $ax0.d, $ax1.r, @$arS
|
||||
// xxxx xxxx 11dr 01ss
|
||||
void DSPEmitter::ldn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x1;
|
||||
u8 rreg = (opc >> 4) & 0x1;
|
||||
u8 sreg = opc & 0x3;
|
||||
|
||||
if (sreg != DSP_REG_AR3) {
|
||||
pushExtValueFromMem((dreg << 1) + DSP_REG_AXL0, sreg);
|
||||
|
||||
//if (IsSameMemArea(g_dsp.r[sreg], g_dsp.r[DSP_REG_AR3])) {
|
||||
dsp_op_read_reg(sreg, RSI, NONE);
|
||||
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
|
||||
SHR(16, R(ESI), Imm8(10));
|
||||
SHR(16, R(EDI), Imm8(10));
|
||||
DSPJitRegCache c(gpr);
|
||||
CMP(16, R(ESI), R(EDI));
|
||||
FixupBranch not_equal = J_CC(CC_NE,true);
|
||||
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, sreg);
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch after = J(true);
|
||||
SetJumpTarget(not_equal); // else
|
||||
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, DSP_REG_AR3);
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(after);
|
||||
|
||||
increase_addr_reg(sreg);
|
||||
} else {
|
||||
pushExtValueFromMem(rreg + DSP_REG_AXH0, dreg);
|
||||
|
||||
//if (IsSameMemArea(g_dsp.r[dreg], g_dsp.r[DSP_REG_AR3])) {
|
||||
dsp_op_read_reg(dreg, RSI, NONE);
|
||||
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
|
||||
SHR(16, R(ESI), Imm8(10));
|
||||
SHR(16, R(EDI), Imm8(10));
|
||||
DSPJitRegCache c(gpr);
|
||||
CMP(16, R(ESI), R(EDI));
|
||||
FixupBranch not_equal = J_CC(CC_NE,true);
|
||||
pushExtValueFromMem2(rreg + DSP_REG_AXL0, dreg);
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch after = J(true); // else
|
||||
SetJumpTarget(not_equal);
|
||||
pushExtValueFromMem2(rreg + DSP_REG_AXL0, DSP_REG_AR3);
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(after);
|
||||
|
||||
increase_addr_reg(dreg);
|
||||
}
|
||||
|
||||
increment_addr_reg(DSP_REG_AR3);
|
||||
}
|
||||
|
||||
// LDM $ax0.d, $ax1.r, @$arS
|
||||
// xxxx xxxx 11dr 10ss
|
||||
void DSPEmitter::ldm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x1;
|
||||
u8 rreg = (opc >> 4) & 0x1;
|
||||
u8 sreg = opc & 0x3;
|
||||
|
||||
if (sreg != DSP_REG_AR3) {
|
||||
pushExtValueFromMem((dreg << 1) + DSP_REG_AXL0, sreg);
|
||||
|
||||
//if (IsSameMemArea(g_dsp.r[sreg], g_dsp.r[DSP_REG_AR3])) {
|
||||
dsp_op_read_reg(sreg, RSI, NONE);
|
||||
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
|
||||
SHR(16, R(ESI), Imm8(10));
|
||||
SHR(16, R(EDI), Imm8(10));
|
||||
DSPJitRegCache c(gpr);
|
||||
CMP(16, R(ESI), R(EDI));
|
||||
FixupBranch not_equal = J_CC(CC_NE,true);
|
||||
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, sreg);
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch after = J(true);
|
||||
SetJumpTarget(not_equal); // else
|
||||
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, DSP_REG_AR3);
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(after);
|
||||
|
||||
increment_addr_reg(sreg);
|
||||
} else {
|
||||
pushExtValueFromMem(rreg + DSP_REG_AXH0, dreg);
|
||||
|
||||
//if (IsSameMemArea(g_dsp.r[dreg], g_dsp.r[DSP_REG_AR3])) {
|
||||
dsp_op_read_reg(dreg, RSI, NONE);
|
||||
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
|
||||
SHR(16, R(ESI), Imm8(10));
|
||||
SHR(16, R(EDI), Imm8(10));
|
||||
DSPJitRegCache c(gpr);
|
||||
CMP(16, R(ESI), R(EDI));
|
||||
FixupBranch not_equal = J_CC(CC_NE,true);
|
||||
pushExtValueFromMem2(rreg + DSP_REG_AXL0, dreg);
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch after = J(true); // else
|
||||
SetJumpTarget(not_equal);
|
||||
pushExtValueFromMem2(rreg + DSP_REG_AXL0, DSP_REG_AR3);
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(after);
|
||||
|
||||
increment_addr_reg(dreg);
|
||||
}
|
||||
|
||||
increase_addr_reg(DSP_REG_AR3);
|
||||
}
|
||||
|
||||
// LDNM $ax0.d, $ax1.r, @$arS
|
||||
// xxxx xxxx 11dr 11ss
|
||||
void DSPEmitter::ldnm(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x1;
|
||||
u8 rreg = (opc >> 4) & 0x1;
|
||||
u8 sreg = opc & 0x3;
|
||||
|
||||
if (sreg != DSP_REG_AR3) {
|
||||
pushExtValueFromMem((dreg << 1) + DSP_REG_AXL0, sreg);
|
||||
|
||||
//if (IsSameMemArea(g_dsp.r[sreg], g_dsp.r[DSP_REG_AR3])) {
|
||||
dsp_op_read_reg(sreg, RSI, NONE);
|
||||
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
|
||||
SHR(16, R(ESI), Imm8(10));
|
||||
SHR(16, R(EDI), Imm8(10));
|
||||
DSPJitRegCache c(gpr);
|
||||
CMP(16, R(ESI), R(EDI));
|
||||
FixupBranch not_equal = J_CC(CC_NE,true);
|
||||
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, sreg);
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch after = J(true);
|
||||
SetJumpTarget(not_equal); // else
|
||||
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, DSP_REG_AR3);
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(after);
|
||||
|
||||
increase_addr_reg(sreg);
|
||||
} else {
|
||||
pushExtValueFromMem(rreg + DSP_REG_AXH0, dreg);
|
||||
|
||||
//if (IsSameMemArea(g_dsp.r[dreg], g_dsp.r[DSP_REG_AR3])) {
|
||||
dsp_op_read_reg(dreg, RSI, NONE);
|
||||
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
|
||||
SHR(16, R(ESI), Imm8(10));
|
||||
SHR(16, R(EDI), Imm8(10));
|
||||
DSPJitRegCache c(gpr);
|
||||
CMP(16, R(ESI), R(EDI));
|
||||
FixupBranch not_equal = J_CC(CC_NE,true);
|
||||
pushExtValueFromMem2(rreg + DSP_REG_AXL0, dreg);
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch after = J(true); // else
|
||||
SetJumpTarget(not_equal);
|
||||
pushExtValueFromMem2(rreg + DSP_REG_AXL0, DSP_REG_AR3);
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(after);
|
||||
|
||||
increase_addr_reg(dreg);
|
||||
}
|
||||
|
||||
increase_addr_reg(DSP_REG_AR3);
|
||||
}
|
||||
|
||||
|
||||
// Push value from g_dsp.r[sreg] into EBX and stores the destinationindex in
|
||||
// storeIndex
|
||||
void DSPEmitter::pushExtValueFromReg(u16 dreg, u16 sreg) {
|
||||
dsp_op_read_reg(sreg, RBX, ZERO);
|
||||
storeIndex = dreg;
|
||||
}
|
||||
|
||||
void DSPEmitter::pushExtValueFromMem(u16 dreg, u16 sreg) {
|
||||
// u16 addr = g_dsp.r[addr];
|
||||
dsp_op_read_reg(sreg, RCX, ZERO);
|
||||
dmem_read();
|
||||
MOVZX(32, 16, EBX, R(EAX));
|
||||
|
||||
storeIndex = dreg;
|
||||
}
|
||||
|
||||
void DSPEmitter::pushExtValueFromMem2(u16 dreg, u16 sreg) {
|
||||
// u16 addr = g_dsp.r[addr];
|
||||
dsp_op_read_reg(sreg, RCX, ZERO);
|
||||
dmem_read();
|
||||
SHL(32, R(EAX), Imm8(16));
|
||||
OR(32, R(EBX), R(EAX));
|
||||
|
||||
storeIndex2 = dreg;
|
||||
}
|
||||
|
||||
void DSPEmitter::popExtValueToReg() {
|
||||
// in practise, we rarely ever have a non-NX main op
|
||||
// with an extended op, so the OR here is either
|
||||
// not run (storeIndex == -1) or ends up OR'ing
|
||||
// EBX with 0 (becoming the MOV we have here)
|
||||
// nakee wants to keep it clean, so lets do that.
|
||||
// [nakeee] the or case never happens in real
|
||||
// [nakeee] it's just how the hardware works so we added it
|
||||
if (storeIndex != -1) {
|
||||
dsp_op_write_reg(storeIndex, RBX);
|
||||
if (storeIndex >= DSP_REG_ACM0 && storeIndex2 == -1) {
|
||||
TEST(32, R(EBX), Imm32(SR_40_MODE_BIT << 16));
|
||||
FixupBranch not_40bit = J_CC(CC_Z);
|
||||
DSPJitRegCache c(gpr);
|
||||
//if (g_dsp.r[DSP_REG_SR] & SR_40_MODE_BIT)
|
||||
//{
|
||||
// Sign extend into whole accum.
|
||||
//u16 val = g_dsp.r[reg];
|
||||
MOVSX(32, 16, EAX, R(EBX));
|
||||
SHR(32, R(EAX), Imm8(16));
|
||||
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACH0] = (val & 0x8000) ? 0xFFFF : 0x0000;
|
||||
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACL0] = 0;
|
||||
set_acc_h(storeIndex - DSP_REG_ACM0, R(RAX));
|
||||
set_acc_l(storeIndex - DSP_REG_ACM0, Imm16(0));
|
||||
//}
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(not_40bit);
|
||||
}
|
||||
}
|
||||
|
||||
storeIndex = -1;
|
||||
|
||||
if (storeIndex2 != -1) {
|
||||
SHR(32, R(EBX), Imm8(16));
|
||||
dsp_op_write_reg(storeIndex2, RBX);
|
||||
}
|
||||
storeIndex2 = -1;
|
||||
}
|
||||
|
||||
// This function is being called in the main op after all input regs were read
|
||||
// and before it writes into any regs. This way we can always use bitwise or to
|
||||
// apply the ext command output, because if the main op didn't change the value
|
||||
// then 0 | ext output = ext output and if it did then bitwise or is still the
|
||||
// right thing to do
|
||||
//this is only needed as long as we do fallback for ext ops
|
||||
void DSPEmitter::zeroWriteBackLog(const UDSPInstruction opc)
|
||||
{
|
||||
const DSPOPCTemplate *tinst = GetOpTemplate(opc);
|
||||
|
||||
// Call extended
|
||||
if (!tinst->extended)
|
||||
return;
|
||||
|
||||
if ((opc >> 12) == 0x3) {
|
||||
if (! extOpTable[opc & 0x7F]->jitFunc)
|
||||
{
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunction((void*)::zeroWriteBackLog);
|
||||
LoadDSPRegs();
|
||||
}
|
||||
} else {
|
||||
if (! extOpTable[opc & 0xFF]->jitFunc)
|
||||
{
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunction((void*)::zeroWriteBackLog);
|
||||
LoadDSPRegs();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
294
Source/Core/Core/Src/DSP/Jit/DSPJitLoadStore.cpp
Normal file
294
Source/Core/Core/Src/DSP/Jit/DSPJitLoadStore.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
#include "../DSPIntCCUtil.h"
|
||||
#include "../DSPIntUtil.h"
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// SRS @M, $(0x18+S)
|
||||
// 0010 1sss mmmm mmmm
|
||||
// Move value from register $(0x18+D) to data memory pointed by address
|
||||
// CR[0-7] | M. That is, the upper 8 bits of the address are the
|
||||
// bottom 8 bits from CR, and the lower 8 bits are from the 8-bit immediate.
|
||||
// Note: pc+=2 in duddie's doc seems wrong
|
||||
void DSPEmitter::srs(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = ((opc >> 8) & 0x7) + 0x18;
|
||||
//u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
|
||||
dsp_op_read_reg(reg, RCX, ZERO);
|
||||
dsp_op_read_reg(DSP_REG_CR, RAX, ZERO);
|
||||
SHL(16, R(EAX), Imm8(8));
|
||||
OR(8, R(EAX), Imm8(opc & 0xFF));
|
||||
dmem_write();
|
||||
}
|
||||
|
||||
// LRS $(0x18+D), @M
|
||||
// 0010 0ddd mmmm mmmm
|
||||
// Move value from data memory pointed by address CR[0-7] | M to register
|
||||
// $(0x18+D). That is, the upper 8 bits of the address are the bottom 8 bits
|
||||
// from CR, and the lower 8 bits are from the 8-bit immediate.
|
||||
void DSPEmitter::lrs(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = ((opc >> 8) & 0x7) + 0x18;
|
||||
//u16 addr = (g_dsp.r[DSP_REG_CR] << 8) | (opc & 0xFF);
|
||||
dsp_op_read_reg(DSP_REG_CR, RCX, ZERO);
|
||||
SHL(16, R(ECX), Imm8(8));
|
||||
OR(8, R(ECX), Imm8(opc & 0xFF));
|
||||
dmem_read();
|
||||
dsp_op_write_reg(reg, RAX);
|
||||
dsp_conditional_extend_accum(reg);
|
||||
}
|
||||
|
||||
// LR $D, @M
|
||||
// 0000 0000 110d dddd
|
||||
// mmmm mmmm mmmm mmmm
|
||||
// Move value from data memory pointed by address M to register $D.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void DSPEmitter::lr(const UDSPInstruction opc)
|
||||
{
|
||||
int reg = opc & DSP_REG_MASK;
|
||||
u16 address = dsp_imem_read(compilePC + 1);
|
||||
dmem_read_imm(address);
|
||||
dsp_op_write_reg(reg, EAX);
|
||||
dsp_conditional_extend_accum(reg);
|
||||
}
|
||||
|
||||
// SR @M, $S
|
||||
// 0000 0000 111s ssss
|
||||
// mmmm mmmm mmmm mmmm
|
||||
// Store value from register $S to a memory pointed by address M.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void DSPEmitter::sr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = opc & DSP_REG_MASK;
|
||||
u16 address = dsp_imem_read(compilePC + 1);
|
||||
dsp_op_read_reg(reg, ECX);
|
||||
dmem_write_imm(address);
|
||||
}
|
||||
|
||||
// SI @M, #I
|
||||
// 0001 0110 mmmm mmmm
|
||||
// iiii iiii iiii iiii
|
||||
// Store 16-bit immediate value I to a memory location pointed by address
|
||||
// M (M is 8-bit value sign extended).
|
||||
void DSPEmitter::si(const UDSPInstruction opc)
|
||||
{
|
||||
u16 address = (s8)opc;
|
||||
u16 imm = dsp_imem_read(compilePC + 1);
|
||||
MOV(32, R(ECX), Imm32((u32)imm));
|
||||
dmem_write_imm(address);
|
||||
}
|
||||
|
||||
// LRR $D, @$S
|
||||
// 0001 1000 0ssd dddd
|
||||
// Move value from data memory pointed by addressing register $S to register $D.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void DSPEmitter::lrr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 5) & 0x3;
|
||||
u8 dreg = opc & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, ECX);
|
||||
dmem_read();
|
||||
dsp_op_write_reg(dreg, EAX);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
}
|
||||
|
||||
// LRRD $D, @$S
|
||||
// 0001 1000 1ssd dddd
|
||||
// Move value from data memory pointed by addressing register $S toregister $D.
|
||||
// Decrement register $S.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void DSPEmitter::lrrd(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 5) & 0x3;
|
||||
u8 dreg = opc & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, ECX);
|
||||
dmem_read();
|
||||
dsp_op_write_reg(dreg, EAX);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
decrement_addr_reg(sreg);
|
||||
}
|
||||
|
||||
// LRRI $D, @$S
|
||||
// 0001 1001 0ssd dddd
|
||||
// Move value from data memory pointed by addressing register $S to register $D.
|
||||
// Increment register $S.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void DSPEmitter::lrri(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 5) & 0x3;
|
||||
u8 dreg = opc & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, ECX);
|
||||
dmem_read();
|
||||
dsp_op_write_reg(dreg, EAX);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
increment_addr_reg(sreg);
|
||||
}
|
||||
|
||||
// LRRN $D, @$S
|
||||
// 0001 1001 1ssd dddd
|
||||
// Move value from data memory pointed by addressing register $S to register $D.
|
||||
// Add indexing register $(0x4+S) to register $S.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void DSPEmitter::lrrn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = (opc >> 5) & 0x3;
|
||||
u8 dreg = opc & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, ECX);
|
||||
dmem_read();
|
||||
dsp_op_write_reg(dreg, EAX);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
increase_addr_reg(sreg);
|
||||
}
|
||||
|
||||
// SRR @$D, $S
|
||||
// 0001 1010 0dds ssss
|
||||
// Store value from source register $S to a memory location pointed by
|
||||
// addressing register $D.
|
||||
// FIXME: Perform additional operation depending on source register.
|
||||
void DSPEmitter::srr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x3;
|
||||
u8 sreg = opc & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, ECX);
|
||||
dsp_op_read_reg(dreg, RAX, ZERO);
|
||||
dmem_write();
|
||||
}
|
||||
|
||||
// SRRD @$D, $S
|
||||
// 0001 1010 1dds ssss
|
||||
// Store value from source register $S to a memory location pointed by
|
||||
// addressing register $D. Decrement register $D.
|
||||
// FIXME: Perform additional operation depending on source register.
|
||||
void DSPEmitter::srrd(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x3;
|
||||
u8 sreg = opc & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, ECX);
|
||||
dsp_op_read_reg(dreg, RAX, ZERO);
|
||||
dmem_write();
|
||||
decrement_addr_reg(dreg);
|
||||
}
|
||||
|
||||
// SRRI @$D, $S
|
||||
// 0001 1011 0dds ssss
|
||||
// Store value from source register $S to a memory location pointed by
|
||||
// addressing register $D. Increment register $D.
|
||||
// FIXME: Perform additional operation depending on source register.
|
||||
void DSPEmitter::srri(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x3;
|
||||
u8 sreg = opc & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, ECX);
|
||||
dsp_op_read_reg(dreg, RAX, ZERO);
|
||||
dmem_write();
|
||||
increment_addr_reg(dreg);
|
||||
}
|
||||
|
||||
// SRRN @$D, $S
|
||||
// 0001 1011 1dds ssss
|
||||
// Store value from source register $S to a memory location pointed by
|
||||
// addressing register $D. Add DSP_REG_IX0 register to register $D.
|
||||
// FIXME: Perform additional operation depending on source register.
|
||||
void DSPEmitter::srrn(const UDSPInstruction opc)
|
||||
{
|
||||
u8 dreg = (opc >> 5) & 0x3;
|
||||
u8 sreg = opc & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, ECX);
|
||||
dsp_op_read_reg(dreg, RAX, ZERO);
|
||||
dmem_write();
|
||||
increase_addr_reg(dreg);
|
||||
}
|
||||
|
||||
// ILRR $acD.m, @$arS
|
||||
// 0000 001d 0001 00ss
|
||||
// Move value from instruction memory pointed by addressing register
|
||||
// $arS to mid accumulator register $acD.m.
|
||||
void DSPEmitter::ilrr(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x3;
|
||||
u16 dreg = (opc >> 8) & 1;
|
||||
|
||||
dsp_op_read_reg(reg, RCX, ZERO);
|
||||
imem_read();
|
||||
set_acc_m(dreg, R(RAX));
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
}
|
||||
|
||||
// ILRRD $acD.m, @$arS
|
||||
// 0000 001d 0001 01ss
|
||||
// Move value from instruction memory pointed by addressing register
|
||||
// $arS to mid accumulator register $acD.m. Decrement addressing register $arS.
|
||||
void DSPEmitter::ilrrd(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x3;
|
||||
u16 dreg = (opc >> 8) & 1;
|
||||
|
||||
dsp_op_read_reg(reg, RCX, ZERO);
|
||||
imem_read();
|
||||
set_acc_m(dreg, R(RAX));
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
decrement_addr_reg(reg);
|
||||
}
|
||||
|
||||
// ILRRI $acD.m, @$S
|
||||
// 0000 001d 0001 10ss
|
||||
// Move value from instruction memory pointed by addressing register
|
||||
// $arS to mid accumulator register $acD.m. Increment addressing register $arS.
|
||||
void DSPEmitter::ilrri(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x3;
|
||||
u16 dreg = (opc >> 8) & 1;
|
||||
|
||||
dsp_op_read_reg(reg, RCX, ZERO);
|
||||
imem_read();
|
||||
set_acc_m(dreg, R(RAX));
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
increment_addr_reg(reg);
|
||||
}
|
||||
|
||||
// ILRRN $acD.m, @$arS
|
||||
// 0000 001d 0001 11ss
|
||||
// Move value from instruction memory pointed by addressing register
|
||||
// $arS to mid accumulator register $acD.m. Add corresponding indexing
|
||||
// register $ixS to addressing register $arS.
|
||||
void DSPEmitter::ilrrn(const UDSPInstruction opc)
|
||||
{
|
||||
u16 reg = opc & 0x3;
|
||||
u16 dreg = (opc >> 8) & 1;
|
||||
|
||||
dsp_op_read_reg(reg, RCX, ZERO);
|
||||
imem_read();
|
||||
set_acc_m(dreg, R(RAX));
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
increase_addr_reg(reg);
|
||||
}
|
||||
|
||||
468
Source/Core/Core/Src/DSP/Jit/DSPJitMisc.cpp
Normal file
468
Source/Core/Core/Src/DSP/Jit/DSPJitMisc.cpp
Normal file
@@ -0,0 +1,468 @@
|
||||
// Copyright (C) 2010 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
|
||||
#include "../DSPIntUtil.h"
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
//clobbers:
|
||||
//EAX = (s8)g_dsp.reg_stack_ptr[stack_reg]
|
||||
//CX = g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]]
|
||||
//expects:
|
||||
//R11 = &g_dsp.r
|
||||
void DSPEmitter::dsp_reg_stack_push(int stack_reg)
|
||||
{
|
||||
//g_dsp.reg_stack_ptr[stack_reg]++;
|
||||
//g_dsp.reg_stack_ptr[stack_reg] &= DSP_STACK_MASK;
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(8, R(AL), M(&g_dsp.reg_stack_ptr[stack_reg]));
|
||||
#else
|
||||
MOV(8, R(AL), MDisp(R11, PtrOffset(&g_dsp.reg_stack_ptr[stack_reg],
|
||||
&g_dsp.r)));
|
||||
#endif
|
||||
ADD(8, R(AL), Imm8(1));
|
||||
AND(8, R(AL), Imm8(DSP_STACK_MASK));
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(8, M(&g_dsp.reg_stack_ptr[stack_reg]), R(AL));
|
||||
#else
|
||||
MOV(8, MDisp(R11, PtrOffset(&g_dsp.reg_stack_ptr[stack_reg],
|
||||
&g_dsp.r)), R(AL));
|
||||
#endif
|
||||
|
||||
//g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]] = g_dsp.r[DSP_REG_ST0 + stack_reg];
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, R(CX), M(&g_dsp.r.st[stack_reg]));
|
||||
MOVZX(32, 8, EAX, R(AL));
|
||||
MOV(16, MComplex(EAX, EAX, 1, (u32)&g_dsp.reg_stack[stack_reg][0]), R(CX));
|
||||
#else
|
||||
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
MOV(16, R(CX), MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])));
|
||||
MOVZX(64, 8, RAX, R(AL));
|
||||
MOV(16, MComplex(R11, RAX, 2,
|
||||
PtrOffset(&g_dsp.reg_stack[stack_reg][0],&g_dsp.r)),
|
||||
R(CX));
|
||||
#endif
|
||||
}
|
||||
|
||||
//clobbers:
|
||||
//EAX = (s8)g_dsp.reg_stack_ptr[stack_reg]
|
||||
//CX = g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]]
|
||||
//expects:
|
||||
//R11 = &g_dsp.r
|
||||
void DSPEmitter::dsp_reg_stack_pop(int stack_reg)
|
||||
{
|
||||
//g_dsp.r[DSP_REG_ST0 + stack_reg] = g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]];
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(8, R(AL), M(&g_dsp.reg_stack_ptr[stack_reg]));
|
||||
#else
|
||||
MOV(8, R(AL),
|
||||
MDisp(R11, PtrOffset(&g_dsp.reg_stack_ptr[stack_reg],&g_dsp.r)));
|
||||
#endif
|
||||
#ifdef _M_IX86 // All32
|
||||
MOVZX(32, 8, EAX, R(AL));
|
||||
MOV(16, R(CX), MComplex(EAX, EAX, 1, (u32)&g_dsp.reg_stack[stack_reg][0]));
|
||||
MOV(16, M(&g_dsp.r.st[stack_reg]), R(CX));
|
||||
#else
|
||||
MOVZX(64, 8, RAX, R(AL));
|
||||
MOV(16, R(CX), MComplex(R11, RAX, 2,
|
||||
PtrOffset(&g_dsp.reg_stack[stack_reg][0],
|
||||
&g_dsp.r)));
|
||||
MOV(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])), R(CX));
|
||||
#endif
|
||||
|
||||
//g_dsp.reg_stack_ptr[stack_reg]--;
|
||||
//g_dsp.reg_stack_ptr[stack_reg] &= DSP_STACK_MASK;
|
||||
SUB(8, R(AL), Imm8(1));
|
||||
AND(8, R(AL), Imm8(DSP_STACK_MASK));
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(8, M(&g_dsp.reg_stack_ptr[stack_reg]), R(AL));
|
||||
#else
|
||||
MOV(8, MDisp(R11, PtrOffset(&g_dsp.reg_stack_ptr[stack_reg],&g_dsp.r)),
|
||||
R(AL));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void DSPEmitter::dsp_reg_store_stack(int stack_reg, Gen::X64Reg host_sreg)
|
||||
{
|
||||
if (host_sreg != EDX) {
|
||||
MOV(16, R(EDX), R(host_sreg));
|
||||
}
|
||||
dsp_reg_stack_push(stack_reg);
|
||||
//g_dsp.r[DSP_REG_ST0 + stack_reg] = val;
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.r.st[stack_reg]), R(EDX));
|
||||
#else
|
||||
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
MOV(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])), R(EDX));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DSPEmitter::dsp_reg_load_stack(int stack_reg, Gen::X64Reg host_dreg)
|
||||
{
|
||||
//u16 val = g_dsp.r[DSP_REG_ST0 + stack_reg];
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, R(EDX), M(&g_dsp.r.st[stack_reg]));
|
||||
#else
|
||||
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
MOV(16, R(EDX), MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])));
|
||||
#endif
|
||||
dsp_reg_stack_pop(stack_reg);
|
||||
if (host_dreg != EDX) {
|
||||
MOV(16, R(host_dreg), R(EDX));
|
||||
}
|
||||
}
|
||||
|
||||
void DSPEmitter::dsp_reg_store_stack_imm(int stack_reg, u16 val)
|
||||
{
|
||||
dsp_reg_stack_push(stack_reg);
|
||||
//g_dsp.r[DSP_REG_ST0 + stack_reg] = val;
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.r.st[stack_reg]), Imm16(val));
|
||||
#else
|
||||
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
MOV(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])), Imm16(val));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DSPEmitter::dsp_op_write_reg(int reg, Gen::X64Reg host_sreg)
|
||||
{
|
||||
switch (reg & 0x1f) {
|
||||
// 8-bit sign extended registers.
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
gpr.writeReg(reg, R(host_sreg));
|
||||
break;
|
||||
|
||||
// Stack registers.
|
||||
case DSP_REG_ST0:
|
||||
case DSP_REG_ST1:
|
||||
case DSP_REG_ST2:
|
||||
case DSP_REG_ST3:
|
||||
dsp_reg_store_stack(reg - DSP_REG_ST0, host_sreg);
|
||||
break;
|
||||
|
||||
default:
|
||||
gpr.writeReg(reg, R(host_sreg));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DSPEmitter::dsp_op_write_reg_imm(int reg, u16 val)
|
||||
{
|
||||
switch (reg & 0x1f) {
|
||||
// 8-bit sign extended registers. Should look at prod.h too...
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
gpr.writeReg(reg, Imm16((u16)(s16)(s8)(u8)val));
|
||||
break;
|
||||
// Stack registers.
|
||||
case DSP_REG_ST0:
|
||||
case DSP_REG_ST1:
|
||||
case DSP_REG_ST2:
|
||||
case DSP_REG_ST3:
|
||||
dsp_reg_store_stack_imm(reg - DSP_REG_ST0, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
gpr.writeReg(reg, Imm16(val));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DSPEmitter::dsp_conditional_extend_accum(int reg)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
{
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
DSPJitRegCache c(gpr);
|
||||
TEST(16, sr_reg, Imm16(SR_40_MODE_BIT));
|
||||
FixupBranch not_40bit = J_CC(CC_Z,true);
|
||||
//if (g_dsp.r[DSP_REG_SR] & SR_40_MODE_BIT)
|
||||
//{
|
||||
// Sign extend into whole accum.
|
||||
//u16 val = g_dsp.r[reg];
|
||||
get_acc_m(reg - DSP_REG_ACM0, EAX);
|
||||
SHR(32, R(EAX), Imm8(16));
|
||||
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACH0] = (val & 0x8000) ? 0xFFFF : 0x0000;
|
||||
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACL0] = 0;
|
||||
set_acc_h(reg - DSP_REG_ACM0, R(RAX));
|
||||
set_acc_l(reg - DSP_REG_ACM0, Imm16(0));
|
||||
//}
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(not_40bit);
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSPEmitter::dsp_conditional_extend_accum_imm(int reg, u16 val)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
{
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
DSPJitRegCache c(gpr);
|
||||
TEST(16, sr_reg, Imm16(SR_40_MODE_BIT));
|
||||
FixupBranch not_40bit = J_CC(CC_Z);
|
||||
//if (g_dsp.r[DSP_REG_SR] & SR_40_MODE_BIT)
|
||||
//{
|
||||
// Sign extend into whole accum.
|
||||
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACH0] = (val & 0x8000) ? 0xFFFF : 0x0000;
|
||||
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACL0] = 0;
|
||||
set_acc_h(reg - DSP_REG_ACM0, Imm16((val & 0x8000)?0xffff:0x0000));
|
||||
set_acc_l(reg - DSP_REG_ACM0, Imm16(0));
|
||||
//}
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(not_40bit);
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSPEmitter::dsp_op_read_reg(int reg, Gen::X64Reg host_dreg, DSPJitSignExtend extend)
|
||||
{
|
||||
switch (reg & 0x1f) {
|
||||
case DSP_REG_ST0:
|
||||
case DSP_REG_ST1:
|
||||
case DSP_REG_ST2:
|
||||
case DSP_REG_ST3:
|
||||
dsp_reg_load_stack(reg - DSP_REG_ST0, host_dreg);
|
||||
switch(extend) {
|
||||
case SIGN:
|
||||
#ifdef _M_IX86 // All32
|
||||
MOVSX(32, 16, host_dreg, R(host_dreg));
|
||||
#else
|
||||
MOVSX(64, 16, host_dreg, R(host_dreg));
|
||||
#endif
|
||||
break;
|
||||
case ZERO:
|
||||
#ifdef _M_IX86 // All32
|
||||
MOVZX(32, 16, host_dreg, R(host_dreg));
|
||||
#else
|
||||
MOVZX(64, 16, host_dreg, R(host_dreg));
|
||||
#endif
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
gpr.readReg(reg, host_dreg, extend);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// MRR $D, $S
|
||||
// 0001 11dd ddds ssss
|
||||
// Move value from register $S to register $D.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void DSPEmitter::mrr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 sreg = opc & 0x1f;
|
||||
u8 dreg = (opc >> 5) & 0x1f;
|
||||
|
||||
dsp_op_read_reg(sreg, EDX);
|
||||
dsp_op_write_reg(dreg, EDX);
|
||||
dsp_conditional_extend_accum(dreg);
|
||||
}
|
||||
|
||||
// LRI $D, #I
|
||||
// 0000 0000 100d dddd
|
||||
// iiii iiii iiii iiii
|
||||
// Load immediate value I to register $D.
|
||||
//
|
||||
// DSPSpy discovery: This, and possibly other instructions that load a
|
||||
// register, has a different behaviour in S40 mode if loaded to AC0.M: The
|
||||
// value gets sign extended to the whole accumulator! This does not happen in
|
||||
// S16 mode.
|
||||
void DSPEmitter::lri(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = opc & DSP_REG_MASK;
|
||||
u16 imm = dsp_imem_read(compilePC+1);
|
||||
dsp_op_write_reg_imm(reg, imm);
|
||||
dsp_conditional_extend_accum_imm(reg, imm);
|
||||
}
|
||||
|
||||
// LRIS $(0x18+D), #I
|
||||
// 0000 1ddd iiii iiii
|
||||
// Load immediate value I (8-bit sign extended) to accumulator register.
|
||||
// FIXME: Perform additional operation depending on destination register.
|
||||
void DSPEmitter::lris(const UDSPInstruction opc)
|
||||
{
|
||||
u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
|
||||
u16 imm = (s8)opc;
|
||||
dsp_op_write_reg_imm(reg, imm);
|
||||
dsp_conditional_extend_accum_imm(reg, imm);
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// NX
|
||||
// 1000 -000 xxxx xxxx
|
||||
// No operation, but can be extended with extended opcode.
|
||||
// This opcode is supposed to do nothing - it's used if you want to use
|
||||
// an opcode extension but not do anything. At least according to duddie.
|
||||
void DSPEmitter::nx(const UDSPInstruction opc)
|
||||
{
|
||||
zeroWriteBackLog(opc);
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// DAR $arD
|
||||
// 0000 0000 0000 01dd
|
||||
// Decrement address register $arD.
|
||||
void DSPEmitter::dar(const UDSPInstruction opc)
|
||||
{
|
||||
// g_dsp.r[opc & 0x3] = dsp_decrement_addr_reg(opc & 0x3);
|
||||
decrement_addr_reg(opc & 0x3);
|
||||
|
||||
}
|
||||
|
||||
// IAR $arD
|
||||
// 0000 0000 0000 10dd
|
||||
// Increment address register $arD.
|
||||
void DSPEmitter::iar(const UDSPInstruction opc)
|
||||
{
|
||||
// g_dsp.r[opc & 0x3] = dsp_increment_addr_reg(opc & 0x3);
|
||||
increment_addr_reg(opc & 0x3);
|
||||
}
|
||||
|
||||
// SUBARN $arD
|
||||
// 0000 0000 0000 11dd
|
||||
// Subtract indexing register $ixD from an addressing register $arD.
|
||||
// used only in IPL-NTSC ucode
|
||||
void DSPEmitter::subarn(const UDSPInstruction opc)
|
||||
{
|
||||
// u8 dreg = opc & 0x3;
|
||||
// g_dsp.r[dreg] = dsp_decrease_addr_reg(dreg, (s16)g_dsp.r[DSP_REG_IX0 + dreg]);
|
||||
decrease_addr_reg(opc & 0x3);
|
||||
}
|
||||
|
||||
// ADDARN $arD, $ixS
|
||||
// 0000 0000 0001 ssdd
|
||||
// Adds indexing register $ixS to an addressing register $arD.
|
||||
// It is critical for the Zelda ucode that this one wraps correctly.
|
||||
void DSPEmitter::addarn(const UDSPInstruction opc)
|
||||
{
|
||||
// u8 dreg = opc & 0x3;
|
||||
// u8 sreg = (opc >> 2) & 0x3;
|
||||
// g_dsp.r[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r[DSP_REG_IX0 + sreg]);
|
||||
|
||||
// From looking around it is always called with the matching index register
|
||||
increase_addr_reg(opc & 0x3);
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
|
||||
void DSPEmitter::setCompileSR(u16 bit) {
|
||||
|
||||
// g_dsp.r[DSP_REG_SR] |= bit
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
OR(16, sr_reg, Imm16(bit));
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
|
||||
compileSR |= bit;
|
||||
}
|
||||
|
||||
void DSPEmitter::clrCompileSR(u16 bit) {
|
||||
|
||||
// g_dsp.r[DSP_REG_SR] &= bit
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
AND(16, sr_reg, Imm16(~bit));
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
|
||||
compileSR &= ~bit;
|
||||
}
|
||||
// SBCLR #I
|
||||
// 0001 0011 aaaa aiii
|
||||
// bit of status register $sr. Bit number is calculated by adding 6 to
|
||||
// immediate value I.
|
||||
void DSPEmitter::sbclr(const UDSPInstruction opc)
|
||||
{
|
||||
u8 bit = (opc & 0x7) + 6;
|
||||
|
||||
clrCompileSR(1 << bit);
|
||||
}
|
||||
|
||||
// SBSET #I
|
||||
// 0001 0010 aaaa aiii
|
||||
// Set bit of status register $sr. Bit number is calculated by adding 6 to
|
||||
// immediate value I.
|
||||
void DSPEmitter::sbset(const UDSPInstruction opc)
|
||||
{
|
||||
u8 bit = (opc & 0x7) + 6;
|
||||
|
||||
setCompileSR(1 << bit);
|
||||
}
|
||||
|
||||
// 1000 1bbb xxxx xxxx, bbb >= 010
|
||||
// This is a bunch of flag setters, flipping bits in SR. So far so good,
|
||||
// but it's harder to know exactly what effect they have.
|
||||
void DSPEmitter::srbith(const UDSPInstruction opc)
|
||||
{
|
||||
zeroWriteBackLog(opc);
|
||||
switch ((opc >> 8) & 0xf)
|
||||
{
|
||||
// M0/M2 change the multiplier mode (it can multiply by 2 for free).
|
||||
case 0xa: // M2
|
||||
clrCompileSR(SR_MUL_MODIFY);
|
||||
break;
|
||||
case 0xb: // M0
|
||||
setCompileSR(SR_MUL_MODIFY);
|
||||
break;
|
||||
|
||||
// If set, treat multiplicands as unsigned.
|
||||
// If clear, treat them as signed.
|
||||
case 0xc: // CLR15
|
||||
clrCompileSR(SR_MUL_UNSIGNED);
|
||||
break;
|
||||
case 0xd: // SET15
|
||||
setCompileSR(SR_MUL_UNSIGNED);
|
||||
break;
|
||||
|
||||
// Automatic 40-bit sign extension when loading ACx.M.
|
||||
// SET40 changes something very important: see the LRI instruction above.
|
||||
case 0xe: // SET16 (CLR40)
|
||||
clrCompileSR(SR_40_MODE_BIT);
|
||||
break;
|
||||
|
||||
case 0xf: // SET40
|
||||
setCompileSR(SR_40_MODE_BIT);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
885
Source/Core/Core/Src/DSP/Jit/DSPJitMultiplier.cpp
Normal file
885
Source/Core/Core/Src/DSP/Jit/DSPJitMultiplier.cpp
Normal file
@@ -0,0 +1,885 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Additional copyrights go to Duddie and Tratax (c) 2004
|
||||
|
||||
|
||||
// Multiplier and product register control
|
||||
|
||||
#include "../DSPIntUtil.h"
|
||||
#include "../DSPEmitter.h"
|
||||
#include "../DSPAnalyzer.h"
|
||||
#ifdef _M_X64
|
||||
#include "DSPJitUtil.h"
|
||||
#endif
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// Returns s64 in RAX
|
||||
// In: RSI = s16 a, RDI = s16 b
|
||||
void DSPEmitter::multiply()
|
||||
{
|
||||
#ifdef _M_X64
|
||||
// prod = (s16)a * (s16)b; //signed
|
||||
MOV(64, R(EAX), R(RDI));
|
||||
IMUL(64, R(ESI));
|
||||
|
||||
// Conditionally multiply by 2.
|
||||
// if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
TEST(16, sr_reg, Imm16(SR_MUL_MODIFY));
|
||||
FixupBranch noMult2 = J_CC(CC_NZ);
|
||||
// prod <<= 1;
|
||||
SHL(64, R(EAX), Imm8(1));
|
||||
SetJumpTarget(noMult2);
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
// return prod;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns s64 in RAX
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::multiply_add()
|
||||
{
|
||||
// s64 prod = dsp_get_long_prod() + dsp_get_multiply_prod(a, b, sign);
|
||||
multiply();
|
||||
MOV(64, R(RDX), R(RAX));
|
||||
get_long_prod();
|
||||
ADD(64, R(RAX), R(RDX));
|
||||
// return prod;
|
||||
}
|
||||
|
||||
// Returns s64 in RAX
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::multiply_sub()
|
||||
{
|
||||
// s64 prod = dsp_get_long_prod() - dsp_get_multiply_prod(a, b, sign);
|
||||
multiply();
|
||||
MOV(64, R(RDX), R(RAX));
|
||||
get_long_prod();
|
||||
SUB(64, R(RAX), R(RDX));
|
||||
// return prod;
|
||||
}
|
||||
|
||||
// Only MULX family instructions have unsigned/mixed support.
|
||||
// Returns s64 in EAX
|
||||
// In: RSI = s16 a, RDI = s16 b
|
||||
// Returns s64 in RAX
|
||||
void DSPEmitter::multiply_mulx(u8 axh0, u8 axh1)
|
||||
{
|
||||
// s64 result;
|
||||
|
||||
// if ((axh0==0) && (axh1==0))
|
||||
// result = dsp_multiply(val1, val2, 1); // unsigned support ON if both ax?.l regs are used
|
||||
// else if ((axh0==0) && (axh1==1))
|
||||
// result = dsp_multiply(val1, val2, 2); // mixed support ON (u16)axl.0 * (s16)axh.1
|
||||
// else if ((axh0==1) && (axh1==0))
|
||||
// result = dsp_multiply(val2, val1, 2); // mixed support ON (u16)axl.1 * (s16)axh.0
|
||||
// else
|
||||
// result = dsp_multiply(val1, val2, 0); // unsigned support OFF if both ax?.h regs are used
|
||||
|
||||
|
||||
// if ((sign == 1) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) //unsigned
|
||||
OpArg sr_reg;
|
||||
gpr.getReg(DSP_REG_SR,sr_reg);
|
||||
TEST(16, sr_reg, Imm16(SR_MUL_UNSIGNED));
|
||||
FixupBranch unsignedMul = J_CC(CC_NZ);
|
||||
// prod = (s16)a * (s16)b; //signed
|
||||
MOVSX(64, 16, RAX, R(RDI));
|
||||
IMUL(64, R(RSI));
|
||||
FixupBranch signedMul = J();
|
||||
|
||||
SetJumpTarget(unsignedMul);
|
||||
if ((axh0==0) && (axh1==0))
|
||||
{
|
||||
// unsigned support ON if both ax?.l regs are used
|
||||
// prod = (u32)(a * b);
|
||||
MOVZX(64, 16, RSI, R(RSI));
|
||||
MOVZX(64, 16, RAX, R(RDI));
|
||||
MUL(64, R(RSI));
|
||||
}
|
||||
else if ((axh0==0) && (axh1==1))
|
||||
{
|
||||
// mixed support ON (u16)axl.0 * (s16)axh.1
|
||||
// prod = a * (s16)b;
|
||||
MOVZX(64, 16, RAX, R(RSI));
|
||||
IMUL(64, R(RDI));
|
||||
}
|
||||
else if ((axh0==1) && (axh1==0))
|
||||
{
|
||||
// mixed support ON (u16)axl.1 * (s16)axh.0
|
||||
// prod = (s16)a * b;
|
||||
MOVZX(64, 16, RAX, R(RDI));
|
||||
IMUL(64, R(RSI));
|
||||
}
|
||||
else
|
||||
{
|
||||
// unsigned support OFF if both ax?.h regs are used
|
||||
// prod = (s16)a * (s16)b; //signed
|
||||
MOVSX(64, 16, RAX, R(RDI));
|
||||
IMUL(64, R(RSI));
|
||||
}
|
||||
|
||||
SetJumpTarget(signedMul);
|
||||
|
||||
// Conditionally multiply by 2.
|
||||
// if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
|
||||
TEST(16, sr_reg, Imm16(SR_MUL_MODIFY));
|
||||
FixupBranch noMult2 = J_CC(CC_NZ);
|
||||
// prod <<= 1;
|
||||
SHL(64, R(RAX), Imm8(1));
|
||||
SetJumpTarget(noMult2);
|
||||
gpr.putReg(DSP_REG_SR);
|
||||
// return prod;
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// CLRP
|
||||
// 1000 0100 xxxx xxxx
|
||||
// Clears product register $prod.
|
||||
// Magic numbers taken from duddie's doc
|
||||
|
||||
// 00ff_(fff0 + 0010)_0000 = 0100_0000_0000, conveniently, lower 40bits = 0
|
||||
|
||||
// It's not ok, to just zero all of them, correct values should be set because of
|
||||
// direct use of prod regs by AX/AXWII (look @that part of ucode).
|
||||
void DSPEmitter::clrp(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
// g_dsp.r[DSP_REG_PRODL] = 0x0000;
|
||||
// g_dsp.r[DSP_REG_PRODM] = 0xfff0;
|
||||
// g_dsp.r[DSP_REG_PRODH] = 0x00ff;
|
||||
// g_dsp.r[DSP_REG_PRODM2] = 0x0010;
|
||||
//64bit move to memory does not work. use 2 32bits
|
||||
MOV(32, MDisp(R11, STRUCT_OFFSET(g_dsp.r, prod.val)+0), Imm32(0xfff00000U));
|
||||
MOV(32, MDisp(R11, STRUCT_OFFSET(g_dsp.r, prod.val)+4), Imm32(0x001000ffU));
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// TSTPROD
|
||||
// 1000 0101 xxxx xxxx
|
||||
// Test prod regs value.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::tstprod(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
// s64 prod = dsp_get_long_prod();
|
||||
get_long_prod();
|
||||
// Update_SR_Register64(prod);
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MOVP $acD
|
||||
// 0110 111d xxxx xxxx
|
||||
// Moves multiply product from $prod register to accumulator $acD register.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::movp(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 dreg = (opc >> 8) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_prod();
|
||||
get_long_prod();
|
||||
// dsp_set_long_acc(dreg, acc);
|
||||
set_long_acc(dreg);
|
||||
// Update_SR_Register64(acc);
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MOVNP $acD
|
||||
// 0111 111d xxxx xxxx
|
||||
// Moves negative of multiply product from $prod register to accumulator
|
||||
// $acD register.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::movnp(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 dreg = (opc >> 8) & 0x1;
|
||||
|
||||
// s64 acc = -dsp_get_long_prod();
|
||||
get_long_prod();
|
||||
NEG(64, R(EAX));
|
||||
// dsp_set_long_acc(dreg, acc);
|
||||
set_long_acc(dreg);
|
||||
// Update_SR_Register64(acc);
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MOVPZ $acD
|
||||
// 1111 111d xxxx xxxx
|
||||
// Moves multiply product from $prod register to accumulator $acD
|
||||
// register and sets (rounds) $acD.l to 0
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::movpz(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 dreg = (opc >> 8) & 0x01;
|
||||
|
||||
// s64 acc = dsp_get_long_prod_round_prodl();
|
||||
get_long_prod_round_prodl();
|
||||
// dsp_set_long_acc(dreg, acc);
|
||||
set_long_acc(dreg);
|
||||
// Update_SR_Register64(acc);
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ADDPAXZ $acD, $axS
|
||||
// 1111 10sd xxxx xxxx
|
||||
// Adds secondary accumulator $axS to product register and stores result
|
||||
// in accumulator register. Low 16-bits of $acD ($acD.l) are set (round) to 0.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::addpaxz(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 dreg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
// s64 ax = dsp_get_long_acx(sreg);
|
||||
get_long_acx(sreg, RCX);
|
||||
MOV(64, R(RDI), R(RCX));
|
||||
// s64 res = prod + (ax & ~0xffff);
|
||||
MOV(64, R(RDX), Imm64(~0xffff));
|
||||
AND(64, R(RDI), R(RDX));
|
||||
// s64 prod = dsp_get_long_prod_round_prodl();
|
||||
get_long_prod_round_prodl();
|
||||
ADD(64, R(RAX), R(RDI));
|
||||
|
||||
// s64 oldprod = dsp_get_long_prod();
|
||||
// dsp_set_long_acc(dreg, res);
|
||||
// res = dsp_get_long_acc(dreg);
|
||||
// Update_SR_Register64(res, isCarry(oldprod, res), false);
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
get_long_prod(RDX);
|
||||
MOV(64, R(RSI), R(RAX));
|
||||
set_long_acc(dreg, RSI);
|
||||
Update_SR_Register64_Carry();
|
||||
}
|
||||
else
|
||||
{
|
||||
set_long_acc(dreg, RAX);
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MULAXH
|
||||
// 1000 0011 xxxx xxxx
|
||||
// Multiply $ax0.h by $ax0.h
|
||||
void DSPEmitter::mulaxh(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
// s64 prod = dsp_multiply(dsp_get_ax_h(0), dsp_get_ax_h(0));
|
||||
dsp_op_read_reg(DSP_REG_AXH0, RSI, SIGN);
|
||||
MOV(64, R(RDI), R(RSI));
|
||||
multiply();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MUL $axS.l, $axS.h
|
||||
// 1001 s000 xxxx xxxx
|
||||
// Multiply low part $axS.l of secondary accumulator $axS by high part
|
||||
// $axS.h of secondary accumulator $axS (treat them both as signed).
|
||||
void DSPEmitter::mul(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 sreg = (opc >> 11) & 0x1;
|
||||
|
||||
// u16 axl = dsp_get_ax_l(sreg);
|
||||
dsp_op_read_reg(DSP_REG_AXL0+sreg, RSI, SIGN);
|
||||
// u16 axh = dsp_get_ax_h(sreg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+sreg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply(axh, axl);
|
||||
multiply();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULAC $axS.l, $axS.h, $acR
|
||||
// 1001 s10r xxxx xxxx
|
||||
// Add product register to accumulator register $acR. Multiply low part
|
||||
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
|
||||
// accumulator $axS (treat them both as signed).
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulac(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 11) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
|
||||
get_long_acc(rreg);
|
||||
MOV(64, R(RDX), R(RAX));
|
||||
get_long_prod();
|
||||
ADD(64, R(RAX), R(RDX));
|
||||
PUSH(64, R(RAX));
|
||||
// u16 axl = dsp_get_ax_l(sreg);
|
||||
dsp_op_read_reg(DSP_REG_AXL0+sreg, RSI, SIGN);
|
||||
// u16 axh = dsp_get_ax_h(sreg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+sreg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply(axl, axh);
|
||||
multiply();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
POP(64, R(RAX));
|
||||
set_long_acc(rreg);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULMV $axS.l, $axS.h, $acR
|
||||
// 1001 s11r xxxx xxxx
|
||||
// Move product register to accumulator register $acR. Multiply low part
|
||||
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
|
||||
// accumulator $axS (treat them both as signed).
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulmv(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_prod();
|
||||
get_long_prod();
|
||||
PUSH(64, R(RAX));
|
||||
mul(opc);
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
POP(64, R(RAX));
|
||||
set_long_acc(rreg);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULMVZ $axS.l, $axS.h, $acR
|
||||
// 1001 s01r xxxx xxxx
|
||||
// Move product register to accumulator register $acR and clear (round) low part
|
||||
// of accumulator register $acR.l. Multiply low part $axS.l of secondary
|
||||
// accumulator $axS by high part $axS.h of secondary accumulator $axS (treat
|
||||
// them both as signed).
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulmvz(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_prod_round_prodl();
|
||||
get_long_prod_round_prodl(RDX);
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
set_long_acc(rreg, RDX);
|
||||
mul(opc);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64(RDX);
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MULX $ax0.S, $ax1.T
|
||||
// 101s t000 xxxx xxxx
|
||||
// Multiply one part $ax0 by one part $ax1.
|
||||
// Part is selected by S and T bits. Zero selects low part, one selects high part.
|
||||
void DSPEmitter::mulx(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 treg = ((opc >> 11) & 0x1);
|
||||
u8 sreg = ((opc >> 12) & 0x1);
|
||||
|
||||
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
|
||||
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
|
||||
multiply_mulx(sreg, treg);
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULXAC $ax0.S, $ax1.T, $acR
|
||||
// 101s t01r xxxx xxxx
|
||||
// Add product register to accumulator register $acR. Multiply one part
|
||||
// $ax0 by one part $ax1. Part is selected by S and
|
||||
// T bits. Zero selects low part, one selects high part.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulxac(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
|
||||
get_long_acc(rreg, RCX);
|
||||
get_long_prod();
|
||||
ADD(64, R(RCX), R(RAX));
|
||||
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
|
||||
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
|
||||
multiply_mulx(sreg, treg);
|
||||
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
set_long_acc(rreg, RCX);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64(RCX);
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULXMV $ax0.S, $ax1.T, $acR
|
||||
// 101s t11r xxxx xxxx
|
||||
// Move product register to accumulator register $acR. Multiply one part
|
||||
// $ax0 by one part $ax1. Part is selected by S and
|
||||
// T bits. Zero selects low part, one selects high part.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulxmv(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = ((opc >> 8) & 0x1);
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_prod();
|
||||
get_long_prod(RCX);
|
||||
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
|
||||
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
|
||||
multiply_mulx(sreg, treg);
|
||||
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
set_long_acc(rreg, RCX);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64(RCX);
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULXMV $ax0.S, $ax1.T, $acR
|
||||
// 101s t01r xxxx xxxx
|
||||
// Move product register to accumulator register $acR and clear (round) low part
|
||||
// of accumulator register $acR.l. Multiply one part $ax0 by one part $ax1
|
||||
// Part is selected by S and T bits. Zero selects low part,
|
||||
// one selects high part.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulxmvz(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_prod_round_prodl();
|
||||
get_long_prod_round_prodl(RCX);
|
||||
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
|
||||
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
|
||||
multiply_mulx(sreg, treg);
|
||||
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
set_long_acc(rreg, RCX);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64(RCX);
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MULC $acS.m, $axT.h
|
||||
// 110s t000 xxxx xxxx
|
||||
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
|
||||
// secondary accumulator $axS (treat them both as signed).
|
||||
void DSPEmitter::mulc(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
// u16 accm = dsp_get_acc_m(sreg);
|
||||
get_acc_m(sreg, ESI);
|
||||
// u16 axh = dsp_get_ax_h(treg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply(accm, axh);
|
||||
multiply();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULCAC $acS.m, $axT.h, $acR
|
||||
// 110s t10r xxxx xxxx
|
||||
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
|
||||
// secondary accumulator $axS (treat them both as signed). Add product
|
||||
// register before multiplication to accumulator $acR.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulcac(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
|
||||
get_long_acc(rreg);
|
||||
MOV(64, R(RDX), R(RAX));
|
||||
get_long_prod();
|
||||
ADD(64, R(RAX), R(RDX));
|
||||
PUSH(64, R(RAX));
|
||||
// u16 accm = dsp_get_acc_m(sreg);
|
||||
get_acc_m(sreg, ESI);
|
||||
// u16 axh = dsp_get_ax_h(treg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply(accm, axh);
|
||||
multiply();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
POP(64, R(RAX));
|
||||
set_long_acc(rreg);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULCMV $acS.m, $axT.h, $acR
|
||||
// 110s t11r xxxx xxxx
|
||||
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
||||
// secondary accumulator $axT (treat them both as signed). Move product
|
||||
// register before multiplication to accumulator $acR.
|
||||
// possible mistake in duddie's doc axT.h rather than axS.h
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulcmv(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
// s64 acc = dsp_get_long_prod();
|
||||
get_long_prod();
|
||||
PUSH(64, R(RAX));
|
||||
// u16 accm = dsp_get_acc_m(sreg);
|
||||
get_acc_m(sreg, ESI);
|
||||
// u16 axh = dsp_get_ax_h(treg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply(accm, axh);
|
||||
multiply();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
POP(64, R(RAX));
|
||||
set_long_acc(rreg);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MULCMVZ $acS.m, $axT.h, $acR
|
||||
// 110s t01r xxxx xxxx
|
||||
// (fixed possible bug in duddie's description, s->t)
|
||||
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
||||
// secondary accumulator $axT (treat them both as signed). Move product
|
||||
// register before multiplication to accumulator $acR, set (round) low part of
|
||||
// accumulator $acR.l to zero.
|
||||
|
||||
// flags out: --xx xx0x
|
||||
void DSPEmitter::mulcmvz(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 rreg = (opc >> 8) & 0x1;
|
||||
u8 treg = (opc >> 11) & 0x1;
|
||||
u8 sreg = (opc >> 12) & 0x1;
|
||||
|
||||
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
// s64 acc = dsp_get_long_prod_round_prodl();
|
||||
get_long_prod_round_prodl();
|
||||
PUSH(64, R(RAX));
|
||||
// u16 accm = dsp_get_acc_m(sreg);
|
||||
get_acc_m(sreg, ESI);
|
||||
// u16 axh = dsp_get_ax_h(treg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply(accm, axh);
|
||||
multiply();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
// dsp_set_long_acc(rreg, acc);
|
||||
POP(64, R(RAX));
|
||||
set_long_acc(rreg);
|
||||
// Update_SR_Register64(dsp_get_long_acc(rreg));
|
||||
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
|
||||
{
|
||||
Update_SR_Register64();
|
||||
}
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
//----
|
||||
|
||||
// MADDX ax0.S ax1.T
|
||||
// 1110 00st xxxx xxxx
|
||||
// Multiply one part of secondary accumulator $ax0 (selected by S) by
|
||||
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
|
||||
// signed) and add result to product register.
|
||||
void DSPEmitter::maddx(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 treg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
|
||||
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_add(val1, val2);
|
||||
multiply_add();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MSUBX $(0x18+S*2), $(0x19+T*2)
|
||||
// 1110 01st xxxx xxxx
|
||||
// Multiply one part of secondary accumulator $ax0 (selected by S) by
|
||||
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
|
||||
// signed) and subtract result from product register.
|
||||
void DSPEmitter::msubx(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 treg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
|
||||
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
|
||||
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
|
||||
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_sub(val1, val2);
|
||||
multiply_sub();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MADDC $acS.m, $axT.h
|
||||
// 1110 10st xxxx xxxx
|
||||
// Multiply middle part of accumulator $acS.m by high part of secondary
|
||||
// accumulator $axT.h (treat them both as signed) and add result to product
|
||||
// register.
|
||||
void DSPEmitter::maddc(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 treg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
// u16 accm = dsp_get_acc_m(sreg);
|
||||
get_acc_m(sreg, ESI);
|
||||
// u16 axh = dsp_get_ax_h(treg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_add(accm, axh);
|
||||
multiply_add();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MSUBC $acS.m, $axT.h
|
||||
// 1110 11st xxxx xxxx
|
||||
// Multiply middle part of accumulator $acS.m by high part of secondary
|
||||
// accumulator $axT.h (treat them both as signed) and subtract result from
|
||||
// product register.
|
||||
void DSPEmitter::msubc(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 treg = (opc >> 8) & 0x1;
|
||||
u8 sreg = (opc >> 9) & 0x1;
|
||||
|
||||
// u16 accm = dsp_get_acc_m(sreg);
|
||||
get_acc_m(sreg, ESI);
|
||||
// u16 axh = dsp_get_ax_h(treg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_sub(accm, axh);
|
||||
multiply_sub();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MADD $axS.l, $axS.h
|
||||
// 1111 001s xxxx xxxx
|
||||
// Multiply low part $axS.l of secondary accumulator $axS by high part
|
||||
// $axS.h of secondary accumulator $axS (treat them both as signed) and add
|
||||
// result to product register.
|
||||
void DSPEmitter::madd(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 sreg = (opc >> 8) & 0x1;
|
||||
|
||||
// u16 axl = dsp_get_ax_l(sreg);
|
||||
dsp_op_read_reg(DSP_REG_AXL0+sreg, RSI, SIGN);
|
||||
// u16 axh = dsp_get_ax_h(sreg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+sreg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_add(axl, axh);
|
||||
multiply_add();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MSUB $axS.l, $axS.h
|
||||
// 1111 011s xxxx xxxx
|
||||
// Multiply low part $axS.l of secondary accumulator $axS by high part
|
||||
// $axS.h of secondary accumulator $axS (treat them both as signed) and
|
||||
// subtract result from product register.
|
||||
void DSPEmitter::msub(const UDSPInstruction opc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
u8 sreg = (opc >> 8) & 0x1;
|
||||
|
||||
// u16 axl = dsp_get_ax_l(sreg);
|
||||
dsp_op_read_reg(DSP_REG_AXL0+sreg, RSI, SIGN);
|
||||
// u16 axh = dsp_get_ax_h(sreg);
|
||||
dsp_op_read_reg(DSP_REG_AXH0+sreg, RDI, SIGN);
|
||||
// s64 prod = dsp_multiply_sub(axl, axh);
|
||||
multiply_sub();
|
||||
// dsp_set_long_prod(prod);
|
||||
set_long_prod();
|
||||
#else
|
||||
Default(opc);
|
||||
#endif
|
||||
}
|
||||
540
Source/Core/Core/Src/DSP/Jit/DSPJitRegCache.cpp
Normal file
540
Source/Core/Core/Src/DSP/Jit/DSPJitRegCache.cpp
Normal file
@@ -0,0 +1,540 @@
|
||||
// Copyright (C) 2011 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "DSPJitRegCache.h"
|
||||
#include "../DSPEmitter.h"
|
||||
#include "../DSPMemoryMap.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
static u16 *reg_ptr(int reg) {
|
||||
switch(reg) {
|
||||
case DSP_REG_AR0:
|
||||
case DSP_REG_AR1:
|
||||
case DSP_REG_AR2:
|
||||
case DSP_REG_AR3:
|
||||
return &g_dsp.r.ar[reg - DSP_REG_AR0];
|
||||
case DSP_REG_IX0:
|
||||
case DSP_REG_IX1:
|
||||
case DSP_REG_IX2:
|
||||
case DSP_REG_IX3:
|
||||
return &g_dsp.r.ix[reg - DSP_REG_IX0];
|
||||
case DSP_REG_WR0:
|
||||
case DSP_REG_WR1:
|
||||
case DSP_REG_WR2:
|
||||
case DSP_REG_WR3:
|
||||
return &g_dsp.r.wr[reg - DSP_REG_WR0];
|
||||
case DSP_REG_ST0:
|
||||
case DSP_REG_ST1:
|
||||
case DSP_REG_ST2:
|
||||
case DSP_REG_ST3:
|
||||
return &g_dsp.r.st[reg - DSP_REG_ST0];
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
return &g_dsp.r.ac[reg - DSP_REG_ACH0].h;
|
||||
case DSP_REG_CR: return &g_dsp.r.cr;
|
||||
case DSP_REG_SR: return &g_dsp.r.sr;
|
||||
case DSP_REG_PRODL: return &g_dsp.r.prod.l;
|
||||
case DSP_REG_PRODM: return &g_dsp.r.prod.m;
|
||||
case DSP_REG_PRODH: return &g_dsp.r.prod.h;
|
||||
case DSP_REG_PRODM2: return &g_dsp.r.prod.m2;
|
||||
case DSP_REG_AXL0:
|
||||
case DSP_REG_AXL1:
|
||||
return &g_dsp.r.ax[reg - DSP_REG_AXL0].l;
|
||||
case DSP_REG_AXH0:
|
||||
case DSP_REG_AXH1:
|
||||
return &g_dsp.r.ax[reg - DSP_REG_AXH0].h;
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
return &g_dsp.r.ac[reg - DSP_REG_ACL0].l;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
return &g_dsp.r.ac[reg - DSP_REG_ACM0].m;
|
||||
default:
|
||||
_assert_msg_(DSPLLE, 0, "cannot happen");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define ROTATED_REG_ACCS
|
||||
//#undef ROTATED_REG_ACCS
|
||||
|
||||
DSPJitRegCache::DSPJitRegCache(DSPEmitter &_emitter)
|
||||
: emitter(_emitter), temporary(false), merged(false) {
|
||||
for(unsigned int i = 0; i < NUMXREGS; i++) {
|
||||
xregs[i].guest_reg = DSP_REG_STATIC;
|
||||
}
|
||||
xregs[RSP].guest_reg = DSP_REG_STATIC;//stack pointer
|
||||
xregs[RBX].guest_reg = DSP_REG_STATIC;//extended op backing store
|
||||
|
||||
xregs[RBP].guest_reg = DSP_REG_NONE;//definitely usable in dsplle because
|
||||
//all external calls are protected
|
||||
|
||||
#ifdef _M_X64
|
||||
xregs[R8].guest_reg = DSP_REG_STATIC;//acc0
|
||||
xregs[R9].guest_reg = DSP_REG_STATIC;//acc1
|
||||
xregs[R10].guest_reg = DSP_REG_NONE;
|
||||
xregs[R11].guest_reg = DSP_REG_STATIC;//&g_dsp.r
|
||||
xregs[R12].guest_reg = DSP_REG_STATIC;//used for cycle counting
|
||||
xregs[R13].guest_reg = DSP_REG_NONE;
|
||||
xregs[R14].guest_reg = DSP_REG_NONE;
|
||||
xregs[R15].guest_reg = DSP_REG_NONE;
|
||||
#endif
|
||||
|
||||
#ifdef _M_X64
|
||||
acc[0].host_reg = R8;
|
||||
acc[0].shift = 0;
|
||||
acc[0].dirty = false;
|
||||
acc[0].used = false;
|
||||
acc[0].tmp_reg = INVALID_REG;
|
||||
|
||||
acc[1].host_reg = R9;
|
||||
acc[1].shift = 0;
|
||||
acc[1].dirty = false;
|
||||
acc[1].used = false;
|
||||
acc[1].tmp_reg = INVALID_REG;
|
||||
#endif
|
||||
for(unsigned int i = 0; i < 32; i++) {
|
||||
regs[i].mem = reg_ptr(i);
|
||||
regs[i].size = 2;
|
||||
}
|
||||
#ifdef _M_X64
|
||||
regs[DSP_REG_ACC0_64].mem = &g_dsp.r.ac[0].val;
|
||||
regs[DSP_REG_ACC0_64].size = 8;
|
||||
regs[DSP_REG_ACC1_64].mem = &g_dsp.r.ac[1].val;
|
||||
regs[DSP_REG_ACC1_64].size = 8;
|
||||
regs[DSP_REG_PROD_64].mem = &g_dsp.r.prod.val;
|
||||
regs[DSP_REG_PROD_64].size = 8;
|
||||
#endif
|
||||
regs[DSP_REG_AX0_32].mem = &g_dsp.r.ax[0].val;
|
||||
regs[DSP_REG_AX0_32].size = 4;
|
||||
regs[DSP_REG_AX1_32].mem = &g_dsp.r.ax[1].val;
|
||||
regs[DSP_REG_AX1_32].size = 4;
|
||||
for(unsigned int i = 0; i < DSP_REG_MAX_MEM_BACKED+1; i++) {
|
||||
regs[i].dirty = false;
|
||||
#ifdef _M_IX86 // All32
|
||||
regs[i].loc = M(regs[i].mem);
|
||||
#else
|
||||
regs[i].loc = MDisp(R11, PtrOffset(regs[i].mem, &g_dsp.r));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
DSPJitRegCache::DSPJitRegCache(const DSPJitRegCache &cache)
|
||||
: emitter(cache.emitter), temporary(true), merged(false)
|
||||
{
|
||||
memcpy(xregs,cache.xregs,sizeof(xregs));
|
||||
#ifdef _M_X64
|
||||
memcpy(acc,cache.acc,sizeof(acc));
|
||||
#endif
|
||||
memcpy(regs,cache.regs,sizeof(regs));
|
||||
}
|
||||
|
||||
DSPJitRegCache& DSPJitRegCache::operator=(const DSPJitRegCache &cache)
|
||||
{
|
||||
_assert_msg_(DSPLLE, &emitter == &cache.emitter, "emitter does not match");
|
||||
_assert_msg_(DSPLLE, temporary, "register cache not temporary??");
|
||||
merged = false;
|
||||
memcpy(xregs,cache.xregs,sizeof(xregs));
|
||||
#ifdef _M_X64
|
||||
memcpy(acc,cache.acc,sizeof(acc));
|
||||
#endif
|
||||
memcpy(regs,cache.regs,sizeof(regs));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DSPJitRegCache::~DSPJitRegCache()
|
||||
{
|
||||
_assert_msg_(DSPLLE, !temporary || merged, "temporary cache not merged");
|
||||
}
|
||||
|
||||
void DSPJitRegCache::flushRegs(DSPJitRegCache &cache, bool emit)
|
||||
{
|
||||
cache.merged = true;
|
||||
|
||||
#ifdef _M_X64
|
||||
for(unsigned int i = 0; i < 2; i++) {
|
||||
if (acc[i].shift > cache.acc[i].shift) {
|
||||
if (emit)
|
||||
emitter.ROL(64, R(acc[i].host_reg),
|
||||
Imm8(acc[i].shift-cache.acc[i].shift));
|
||||
acc[i].shift = cache.acc[i].shift;
|
||||
}
|
||||
if (acc[i].shift < cache.acc[i].shift) {
|
||||
if (emit)
|
||||
emitter.ROR(64, R(acc[i].host_reg),
|
||||
Imm8(cache.acc[i].shift-acc[i].shift));
|
||||
acc[i].shift = cache.acc[i].shift;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DSPJitRegCache::drop()
|
||||
{
|
||||
merged = true;
|
||||
}
|
||||
|
||||
void DSPJitRegCache::flushRegs()
|
||||
{
|
||||
//also needs to undo any dynamic changes to static allocated regs
|
||||
//this should have the same effect as
|
||||
//merge(DSPJitRegCache(emitter));
|
||||
#ifdef _M_X64
|
||||
#ifdef ROTATED_REG_ACCS
|
||||
for(unsigned int i = 0; i < 2; i++) {
|
||||
if (acc[i].shift > 0) {
|
||||
emitter.ROL(64, R(acc[i].host_reg),
|
||||
Imm8(acc[i].shift));
|
||||
acc[i].shift = 0;
|
||||
}
|
||||
_assert_msg_(DSPLLE, !acc[i].used,
|
||||
"accumulator still in use");
|
||||
if (acc[i].used)
|
||||
emitter.INT3();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static u64 ebp_store;
|
||||
|
||||
void DSPJitRegCache::loadStaticRegs()
|
||||
{
|
||||
#ifdef _M_X64
|
||||
#ifdef ROTATED_REG_ACCS
|
||||
emitter.MOV(64, R(R8), MDisp(R11, STRUCT_OFFSET(g_dsp.r, ac[0].val)));
|
||||
emitter.MOV(64, R(R9), MDisp(R11, STRUCT_OFFSET(g_dsp.r, ac[1].val)));
|
||||
#endif
|
||||
emitter.MOV(64, MDisp(R11, PtrOffset(&ebp_store, &g_dsp.r)), R(RBP));
|
||||
#else
|
||||
emitter.MOV(32, M(&ebp_store), R(EBP));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DSPJitRegCache::saveStaticRegs()
|
||||
{
|
||||
flushRegs();
|
||||
#ifdef _M_X64
|
||||
#ifdef ROTATED_REG_ACCS
|
||||
emitter.MOV(64, MDisp(R11, STRUCT_OFFSET(g_dsp.r, ac[0].val)), R(R8));
|
||||
emitter.MOV(64, MDisp(R11, STRUCT_OFFSET(g_dsp.r, ac[1].val)), R(R9));
|
||||
#endif
|
||||
emitter.MOV(64, R(RBP), MDisp(R11, PtrOffset(&ebp_store, &g_dsp.r)));
|
||||
#else
|
||||
emitter.MOV(32, R(EBP), M(&ebp_store));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DSPJitRegCache::getReg(int reg, OpArg &oparg, bool load)
|
||||
{
|
||||
switch(reg) {
|
||||
#ifdef _M_X64
|
||||
#ifdef ROTATED_REG_ACCS
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
{
|
||||
_assert_msg_(DSPLLE, !acc[reg-DSP_REG_ACH0].used,
|
||||
"accumulator already in use");
|
||||
if (acc[reg-DSP_REG_ACH0].used)
|
||||
emitter.INT3();
|
||||
oparg = R(acc[reg-DSP_REG_ACH0].host_reg);
|
||||
if (acc[reg-DSP_REG_ACH0].shift < 32) {
|
||||
emitter.ROR(64, oparg, Imm8(32-acc[reg-DSP_REG_ACH0].shift));
|
||||
acc[reg-DSP_REG_ACH0].shift = 32;
|
||||
}
|
||||
|
||||
acc[reg-DSP_REG_ACH0].used = true;
|
||||
}
|
||||
break;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
{
|
||||
_assert_msg_(DSPLLE, !acc[reg-DSP_REG_ACM0].used,
|
||||
"accumulator already in use");
|
||||
if (acc[reg-DSP_REG_ACM0].used)
|
||||
emitter.INT3();
|
||||
oparg = R(acc[reg-DSP_REG_ACM0].host_reg);
|
||||
if (acc[reg-DSP_REG_ACM0].shift < 16) {
|
||||
emitter.ROR(64, oparg, Imm8(16-acc[reg-DSP_REG_ACM0].shift));
|
||||
acc[reg-DSP_REG_ACM0].shift = 16;
|
||||
}
|
||||
if (acc[reg-DSP_REG_ACM0].shift > 16) {
|
||||
emitter.ROL(64, oparg, Imm8(acc[reg-DSP_REG_ACM0].shift-16));
|
||||
acc[reg-DSP_REG_ACM0].shift = 16;
|
||||
}
|
||||
acc[reg-DSP_REG_ACM0].used = true;
|
||||
}
|
||||
break;
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
{
|
||||
_assert_msg_(DSPLLE, !acc[reg-DSP_REG_ACL0].used,
|
||||
"accumulator already in use");
|
||||
if (acc[reg-DSP_REG_ACL0].used)
|
||||
emitter.INT3();
|
||||
oparg = R(acc[reg-DSP_REG_ACL0].host_reg);
|
||||
if (acc[reg-DSP_REG_ACL0].shift > 0) {
|
||||
emitter.ROL(64, oparg, Imm8(acc[reg-DSP_REG_ACL0].shift));
|
||||
acc[reg-DSP_REG_ACL0].shift = 0;
|
||||
}
|
||||
acc[reg-DSP_REG_ACL0].used = true;
|
||||
}
|
||||
break;
|
||||
case DSP_REG_ACC0_64:
|
||||
case DSP_REG_ACC1_64:
|
||||
{
|
||||
if (acc[reg-DSP_REG_ACC0_64].used)
|
||||
emitter.INT3();
|
||||
_assert_msg_(DSPLLE, !acc[reg-DSP_REG_ACC0_64].used,
|
||||
"accumulator already in use");
|
||||
oparg = R(acc[reg-DSP_REG_ACC0_64].host_reg);
|
||||
if (load) {
|
||||
if (acc[reg-DSP_REG_ACC0_64].shift > 0) {
|
||||
emitter.ROL(64, oparg, Imm8(acc[reg-DSP_REG_ACC0_64].shift));
|
||||
}
|
||||
emitter.SHL(64, oparg, Imm8(64-40));//sign extend
|
||||
emitter.SAR(64, oparg, Imm8(64-40));
|
||||
}
|
||||
//don't bother to rotate if caller replaces all data
|
||||
acc[reg-DSP_REG_ACC0_64].shift = 0;
|
||||
acc[reg-DSP_REG_ACC0_64].used = true;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
/*
|
||||
getFreeXReg(reg[reg].host_reg);
|
||||
X64Reg tmp = reg[reg].host_reg;
|
||||
oparg = R(tmp);
|
||||
|
||||
if (load) {
|
||||
u16 *regp = reg_ptr(reg);
|
||||
#ifdef _M_IX86 // All32
|
||||
emitter.MOV(16, oparg, M(regp));
|
||||
#else
|
||||
emitter.MOV(16, oparg, MDisp(R11, PtrOffset(regp, &g_dsp.r)));
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
oparg = regs[reg].loc; //when loading/storing from/to mem, need to consider regs[reg].size
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DSPJitRegCache::putReg(int reg, bool dirty)
|
||||
{
|
||||
switch(reg) {
|
||||
#ifdef _M_X64
|
||||
#ifdef ROTATED_REG_ACCS
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
{
|
||||
|
||||
if (dirty) {
|
||||
if (acc[reg-DSP_REG_ACH0].shift > 0) {
|
||||
emitter.ROL(64, R(acc[reg-DSP_REG_ACH0].host_reg),
|
||||
Imm8(acc[reg-DSP_REG_ACH0].shift));
|
||||
acc[reg-DSP_REG_ACH0].shift = 0;
|
||||
}
|
||||
emitter.SHL(64, R(acc[reg-DSP_REG_ACH0].host_reg), Imm8(64-40));//sign extend
|
||||
emitter.SAR(64, R(acc[reg-DSP_REG_ACH0].host_reg), Imm8(64-40));
|
||||
}
|
||||
acc[reg-DSP_REG_ACH0].used = false;
|
||||
}
|
||||
break;
|
||||
case DSP_REG_ACM0:
|
||||
case DSP_REG_ACM1:
|
||||
{
|
||||
acc[reg-DSP_REG_ACM0].used = false;
|
||||
}
|
||||
break;
|
||||
case DSP_REG_ACL0:
|
||||
case DSP_REG_ACL1:
|
||||
acc[reg-DSP_REG_ACL0].used = false;
|
||||
break;
|
||||
case DSP_REG_ACC0_64:
|
||||
case DSP_REG_ACC1_64:
|
||||
{
|
||||
if (dirty) {
|
||||
OpArg _reg = R(acc[reg-DSP_REG_ACC0_64].host_reg);
|
||||
|
||||
emitter.SHL(64, _reg, Imm8(64-40));//sign extend
|
||||
emitter.SAR(64, _reg, Imm8(64-40));
|
||||
}
|
||||
acc[reg-DSP_REG_ACC0_64].used = false;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
{
|
||||
//need to fix in memory for now.
|
||||
u16 *regp = reg_ptr(reg);
|
||||
OpArg mem;
|
||||
mem = MDisp(R11,PtrOffset(regp,&g_dsp.r));
|
||||
X64Reg tmp;
|
||||
getFreeXReg(tmp);
|
||||
// sign extend from the bottom 8 bits.
|
||||
emitter.MOVSX(16, 8, tmp, mem);
|
||||
emitter.MOV(16, mem, R(tmp));
|
||||
putXReg(tmp);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#else
|
||||
case DSP_REG_ACH0:
|
||||
case DSP_REG_ACH1:
|
||||
{
|
||||
//need to fix in memory for now.
|
||||
u16 *regp = reg_ptr(reg);
|
||||
OpArg mem;
|
||||
mem = M(regp);
|
||||
X64Reg tmp;
|
||||
getFreeXReg(tmp);
|
||||
// sign extend from the bottom 8 bits.
|
||||
emitter.MOVSX(16, 8, tmp, mem);
|
||||
emitter.MOV(16, mem, R(tmp));
|
||||
putXReg(tmp);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
/*
|
||||
X64Reg tmp = reg[reg].host_reg;
|
||||
|
||||
if(dirty) {
|
||||
u16 *regp = reg_ptr(reg);
|
||||
#ifdef _M_IX86 // All32
|
||||
emitter.MOV(16, M(dregp), R(tmp));
|
||||
#else
|
||||
emitter.MOV(16, MDisp(R11, PtrOffset(dregp, &g_dsp.r)), R(tmp));
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DSPJitRegCache::readReg(int sreg, X64Reg host_dreg, DSPJitSignExtend extend)
|
||||
{
|
||||
OpArg reg;
|
||||
getReg(sreg, reg);
|
||||
switch(regs[sreg].size) {
|
||||
case 2:
|
||||
switch(extend) {
|
||||
#ifdef _M_X64
|
||||
case SIGN: emitter.MOVSX(64, 16, host_dreg, reg); break;
|
||||
case ZERO: emitter.MOVZX(64, 16, host_dreg, reg); break;
|
||||
#else
|
||||
case SIGN: emitter.MOVSX(32, 16, host_dreg, reg); break;
|
||||
case ZERO: emitter.MOVZX(32, 16, host_dreg, reg); break;
|
||||
#endif
|
||||
case NONE: emitter.MOV(16, R(host_dreg), reg); break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
#ifdef _M_X64
|
||||
switch(extend) {
|
||||
case SIGN: emitter.MOVSX(64, 32, host_dreg, reg); break;
|
||||
case ZERO: emitter.MOVZX(64, 32, host_dreg, reg); break;
|
||||
case NONE: emitter.MOV(32, R(host_dreg), reg); break;
|
||||
}
|
||||
#else
|
||||
emitter.MOV(32, R(host_dreg), reg); break;
|
||||
#endif
|
||||
break;
|
||||
#ifdef _M_X64
|
||||
case 8:
|
||||
emitter.MOV(64, R(host_dreg), reg); break;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
_assert_msg_(DSPLLE, 0, "unsupported memory size");
|
||||
break;
|
||||
}
|
||||
putReg(sreg, false);
|
||||
}
|
||||
|
||||
void DSPJitRegCache::writeReg(int dreg, OpArg arg)
|
||||
{
|
||||
OpArg reg;
|
||||
getReg(dreg, reg, false);
|
||||
switch(regs[dreg].size) {
|
||||
case 2: emitter.MOV(16, reg, arg); break;
|
||||
case 4: emitter.MOV(32, reg, arg); break;
|
||||
#ifdef _M_X64
|
||||
case 8: emitter.MOV(64, reg, arg); break;
|
||||
#endif
|
||||
default:
|
||||
_assert_msg_(DSPLLE, 0, "unsupported memory size");
|
||||
break;
|
||||
}
|
||||
putReg(dreg, true);
|
||||
}
|
||||
|
||||
X64Reg DSPJitRegCache::spillXReg()
|
||||
{
|
||||
//todo: implement
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
void DSPJitRegCache::spillXReg(X64Reg reg)
|
||||
{
|
||||
//todo: implement
|
||||
}
|
||||
|
||||
X64Reg DSPJitRegCache::findFreeXReg()
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < NUMXREGS; i++) {
|
||||
if (xregs[i].guest_reg == DSP_REG_NONE) {
|
||||
return (X64Reg)i;
|
||||
}
|
||||
}
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
void DSPJitRegCache::getFreeXReg(X64Reg ®)
|
||||
{
|
||||
reg = findFreeXReg();
|
||||
if (reg == INVALID_REG)
|
||||
reg = spillXReg();
|
||||
_assert_msg_(DSPLLE, reg != INVALID_REG, "could not find register");
|
||||
xregs[reg].guest_reg = DSP_REG_USED;
|
||||
}
|
||||
|
||||
void DSPJitRegCache::getXReg(X64Reg reg)
|
||||
{
|
||||
if (xregs[reg].guest_reg != DSP_REG_NONE)
|
||||
spillXReg(reg);
|
||||
_assert_msg_(DSPLLE, xregs[reg].guest_reg != DSP_REG_NONE, "register already in use");
|
||||
xregs[reg].guest_reg = DSP_REG_USED;
|
||||
}
|
||||
|
||||
void DSPJitRegCache::putXReg(X64Reg reg)
|
||||
{
|
||||
_assert_msg_(DSPLLE, xregs[reg].guest_reg == DSP_REG_USED,
|
||||
"putXReg without get(Free)XReg");
|
||||
xregs[reg].guest_reg = DSP_REG_NONE;
|
||||
}
|
||||
169
Source/Core/Core/Src/DSP/Jit/DSPJitRegCache.h
Normal file
169
Source/Core/Core/Src/DSP/Jit/DSPJitRegCache.h
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright (C) 2011 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DSPJITREGCACHE_H
|
||||
#define _DSPJITREGCACHE_H
|
||||
|
||||
#include "x64Emitter.h"
|
||||
|
||||
class DSPEmitter;
|
||||
|
||||
enum DSPJitRegSpecial {
|
||||
DSP_REG_ACC0_64 =32,
|
||||
DSP_REG_ACC1_64 =33,
|
||||
DSP_REG_AX0_32 =34,
|
||||
DSP_REG_AX1_32 =35,
|
||||
DSP_REG_PROD_64 =36,
|
||||
DSP_REG_MAX_MEM_BACKED = 36,
|
||||
|
||||
DSP_REG_USED =253,
|
||||
DSP_REG_STATIC =254,
|
||||
DSP_REG_NONE =255
|
||||
};
|
||||
|
||||
enum DSPJitSignExtend {
|
||||
SIGN, ZERO, NONE
|
||||
};
|
||||
|
||||
#ifdef _M_X64
|
||||
#define NUMXREGS 16
|
||||
#elif _M_IX86
|
||||
#define NUMXREGS 8
|
||||
#endif
|
||||
|
||||
class DSPJitRegCache {
|
||||
private:
|
||||
struct X64CachedReg
|
||||
{
|
||||
int guest_reg; //including DSPJitRegSpecial
|
||||
};
|
||||
struct DynamicReg {
|
||||
Gen::OpArg loc;
|
||||
void *mem;
|
||||
size_t size;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
#ifdef _M_X64
|
||||
//when there is a way to do this efficiently in x86, uncondition
|
||||
struct {
|
||||
Gen::X64Reg host_reg;
|
||||
int shift;
|
||||
bool dirty;
|
||||
bool used;
|
||||
Gen::X64Reg tmp_reg;
|
||||
} acc[2];
|
||||
#endif
|
||||
|
||||
DynamicReg regs[DSP_REG_MAX_MEM_BACKED+1];
|
||||
X64CachedReg xregs[NUMXREGS];
|
||||
|
||||
DSPEmitter &emitter;
|
||||
bool temporary;
|
||||
bool merged;
|
||||
private:
|
||||
//find a free host reg
|
||||
Gen::X64Reg findFreeXReg();
|
||||
Gen::X64Reg spillXReg();
|
||||
void spillXReg(Gen::X64Reg reg);
|
||||
public:
|
||||
DSPJitRegCache(DSPEmitter &_emitter);
|
||||
|
||||
//for branching into multiple control flows
|
||||
DSPJitRegCache(const DSPJitRegCache &cache);
|
||||
DSPJitRegCache& operator=(const DSPJitRegCache &cache);
|
||||
|
||||
~DSPJitRegCache();
|
||||
|
||||
//merge must be done _before_ leaving the code branch, so we can fix
|
||||
//up any differences in state
|
||||
void flushRegs(DSPJitRegCache &cache, bool emit = true);
|
||||
/* since some use cases are non-trivial, some examples:
|
||||
|
||||
//this does not modify the final state of gpr
|
||||
<code using gpr>
|
||||
FixupBranch b = JCC();
|
||||
DSPJitRegCache c = gpr;
|
||||
<code using c>
|
||||
gpr.flushRegs(c);
|
||||
SetBranchTarget(b);
|
||||
<code using gpr>
|
||||
|
||||
//this does not modify the final state of gpr
|
||||
<code using gpr>
|
||||
DSPJitRegCache c = gpr;
|
||||
FixupBranch b1 = JCC();
|
||||
<code using gpr>
|
||||
gpr.flushRegs(c);
|
||||
FixupBranch b2 = JMP();
|
||||
SetBranchTarget(b1);
|
||||
<code using gpr>
|
||||
gpr.flushRegs(c);
|
||||
SetBranchTarget(b2);
|
||||
<code using gpr>
|
||||
|
||||
//this allows gpr to be modified in the second branch
|
||||
//and fixes gpr according to the results form in the first branch
|
||||
<code using gpr>
|
||||
DSPJitRegCache c = gpr;
|
||||
FixupBranch b1 = JCC();
|
||||
<code using c>
|
||||
FixupBranch b2 = JMP();
|
||||
SetBranchTarget(b1);
|
||||
<code using gpr>
|
||||
gpr.flushRegs(c);
|
||||
SetBranchTarget(b2);
|
||||
<code using gpr>
|
||||
|
||||
//this does not modify the final state of gpr
|
||||
<code using gpr>
|
||||
u8* b = GetCodePtr();
|
||||
DSPJitRegCache c = gpr;
|
||||
<code using gpr>
|
||||
gpr.flushRegs(c);
|
||||
JCC(b);
|
||||
<code using gpr>
|
||||
|
||||
this all is not needed when gpr would not be used at all in the
|
||||
conditional branch
|
||||
*/
|
||||
//drop this copy without warning
|
||||
void drop();
|
||||
|
||||
//prepare state so that another flushed DSPJitRegCache can take over
|
||||
void flushRegs();
|
||||
|
||||
void loadStaticRegs();//load statically allocated regs from memory
|
||||
void saveStaticRegs();//save statically allocated regs to memory
|
||||
|
||||
//gives no SCALE_RIP with abs(offset) >= 0x80000000
|
||||
void getReg(int reg, Gen::OpArg &oparg, bool load = true);
|
||||
//done with all usages of OpArg above
|
||||
void putReg(int reg, bool dirty = true);
|
||||
|
||||
void readReg(int sreg, Gen::X64Reg host_dreg, DSPJitSignExtend extend);
|
||||
void writeReg(int dreg, Gen::OpArg arg);
|
||||
|
||||
//find a free host reg, spill if used, reserve
|
||||
void getFreeXReg(Gen::X64Reg ®);
|
||||
//spill a specific host reg if used, reserve
|
||||
void getXReg(Gen::X64Reg reg);
|
||||
//unreserve the given host reg
|
||||
void putXReg(Gen::X64Reg reg);
|
||||
};
|
||||
|
||||
#endif /*_DSPJITREGCACHE_H*/
|
||||
596
Source/Core/Core/Src/DSP/Jit/DSPJitUtil.cpp
Normal file
596
Source/Core/Core/Src/DSP/Jit/DSPJitUtil.cpp
Normal file
@@ -0,0 +1,596 @@
|
||||
// Copyright (C) 2010 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../DSPMemoryMap.h"
|
||||
#include "../DSPHWInterface.h"
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
// addr math
|
||||
//
|
||||
// These functions detect overflow by checking if
|
||||
// the bit past the top of the mask(WR) has changed in AR.
|
||||
// They detect values under the minimum for a mask by adding wr + 1
|
||||
// and checking if the bit past the top of the mask doesn't change.
|
||||
// Both are done while ignoring changes due to values/holes in IX
|
||||
// above the mask.
|
||||
|
||||
|
||||
// EAX = g_dsp.r.ar[reg]
|
||||
// EDX = g_dsp.r.wr[reg]
|
||||
// EDI = temp
|
||||
// ECX = temp
|
||||
void DSPEmitter::increment_addr_reg(int reg)
|
||||
{
|
||||
OpArg ar_reg;
|
||||
OpArg wr_reg;
|
||||
gpr.getReg(DSP_REG_WR0+reg,wr_reg);
|
||||
MOVZX(32, 16, EDX, wr_reg);
|
||||
gpr.putReg(DSP_REG_WR0+reg);
|
||||
gpr.getReg(DSP_REG_AR0+reg,ar_reg);
|
||||
MOVZX(32, 16, EAX, ar_reg);
|
||||
|
||||
//u32 nar = ar + 1;
|
||||
MOV(32, R(EDI), R(EAX));
|
||||
ADD(32, R(EAX), Imm8(1));
|
||||
|
||||
// if ((nar ^ ar) > ((wr | 1) << 1))
|
||||
// nar -= wr + 1;
|
||||
XOR(32, R(EDI), R(EAX));
|
||||
LEA(32, ECX, MComplex(EDX, EDX, 1, 0));
|
||||
OR(32, R(ECX), Imm8(2));
|
||||
CMP(32, R(EDI), R(ECX));
|
||||
FixupBranch nowrap = J_CC(CC_BE);
|
||||
SUB(16, R(AX), R(DX));
|
||||
SUB(16, R(AX), Imm8(1));
|
||||
SetJumpTarget(nowrap);
|
||||
// g_dsp.r.ar[reg] = nar;
|
||||
|
||||
MOV(16, ar_reg, R(AX));
|
||||
gpr.putReg(DSP_REG_AR0+reg);
|
||||
}
|
||||
|
||||
// EAX = g_dsp.r.ar[reg]
|
||||
// EDX = g_dsp.r.wr[reg]
|
||||
// EDI = temp
|
||||
// ECX = temp
|
||||
void DSPEmitter::decrement_addr_reg(int reg)
|
||||
{
|
||||
OpArg ar_reg;
|
||||
OpArg wr_reg;
|
||||
gpr.getReg(DSP_REG_WR0+reg,wr_reg);
|
||||
MOVZX(32, 16, EDX, wr_reg);
|
||||
gpr.putReg(DSP_REG_WR0+reg);
|
||||
gpr.getReg(DSP_REG_AR0+reg,ar_reg);
|
||||
MOVZX(32, 16, EAX, ar_reg);
|
||||
|
||||
// u32 nar = ar + wr;
|
||||
// edi = nar
|
||||
LEA(32, EDI, MComplex(EAX, EDX, 1, 0));
|
||||
|
||||
// if (((nar ^ ar) & ((wr | 1) << 1)) > wr)
|
||||
// nar -= wr + 1;
|
||||
XOR(32, R(EAX), R(EDI));
|
||||
LEA(32, ECX, MComplex(EDX, EDX, 1, 0));
|
||||
OR(32, R(ECX), Imm8(2));
|
||||
AND(32, R(EAX), R(ECX));
|
||||
CMP(32, R(EAX), R(EDX));
|
||||
FixupBranch nowrap = J_CC(CC_BE);
|
||||
SUB(16, R(DI), R(DX));
|
||||
SUB(16, R(DI), Imm8(1));
|
||||
SetJumpTarget(nowrap);
|
||||
// g_dsp.r.ar[reg] = nar;
|
||||
|
||||
MOV(16, ar_reg, R(DI));
|
||||
gpr.putReg(DSP_REG_AR0+reg);
|
||||
}
|
||||
|
||||
// Increase addr register according to the correspond ix register
|
||||
// EAX = g_dsp.r.ar[reg]
|
||||
// EDX = g_dsp.r.wr[reg]
|
||||
// ESI = g_dsp.r.ix[reg]
|
||||
// ECX = temp
|
||||
// EDI = temp
|
||||
void DSPEmitter::increase_addr_reg(int reg)
|
||||
{
|
||||
OpArg ar_reg;
|
||||
OpArg wr_reg;
|
||||
OpArg ix_reg;
|
||||
gpr.getReg(DSP_REG_WR0+reg,wr_reg);
|
||||
gpr.getReg(DSP_REG_IX0+reg,ix_reg);
|
||||
MOVZX(32, 16, EDX, wr_reg);
|
||||
MOVSX(32, 16, ESI, ix_reg);
|
||||
gpr.putReg(DSP_REG_WR0+reg);
|
||||
gpr.putReg(DSP_REG_IX0+reg);
|
||||
gpr.getReg(DSP_REG_AR0+reg,ar_reg);
|
||||
MOVZX(32, 16, EAX, ar_reg);
|
||||
|
||||
//u32 nar = ar + ix;
|
||||
//edi = nar
|
||||
LEA(32, EDI, MComplex(EAX, ESI, 1, 0));
|
||||
|
||||
//u32 dar = (nar ^ ar ^ ix) & ((wr | 1) << 1);
|
||||
//eax = dar
|
||||
XOR(32, R(EAX), R(ESI));
|
||||
XOR(32, R(EAX), R(EDI));
|
||||
LEA(32, ECX, MComplex(EDX, EDX, 1, 0));
|
||||
OR(32, R(ECX), Imm8(2));
|
||||
AND(32, R(EAX), R(ECX));
|
||||
|
||||
//if (ix >= 0)
|
||||
TEST(32, R(ESI), R(ESI));
|
||||
FixupBranch negative = J_CC(CC_S);
|
||||
//if (dar > wr)
|
||||
CMP(32, R(EAX), R(EDX));
|
||||
FixupBranch done = J_CC(CC_BE);
|
||||
//nar -= wr + 1;
|
||||
SUB(16, R(DI), R(DX));
|
||||
SUB(16, R(DI), Imm8(1));
|
||||
FixupBranch done2 = J();
|
||||
|
||||
//else
|
||||
SetJumpTarget(negative);
|
||||
//if ((((nar + wr + 1) ^ nar) & dar) <= wr)
|
||||
LEA(32, ECX, MComplex(EDI, EDX, 1, 1));
|
||||
XOR(32, R(ECX), R(EDI));
|
||||
AND(32, R(ECX), R(EAX));
|
||||
CMP(32, R(ECX), R(EDX));
|
||||
FixupBranch done3 = J_CC(CC_A);
|
||||
//nar += wr + 1;
|
||||
LEA(32, EDI, MComplex(EDI, EDX, 1, 1));
|
||||
|
||||
SetJumpTarget(done);
|
||||
SetJumpTarget(done2);
|
||||
SetJumpTarget(done3);
|
||||
// g_dsp.r.ar[reg] = nar;
|
||||
|
||||
MOV(16, ar_reg, R(DI));
|
||||
gpr.putReg(DSP_REG_AR0+reg);
|
||||
}
|
||||
|
||||
// Decrease addr register according to the correspond ix register
|
||||
// EAX = g_dsp.r.ar[reg]
|
||||
// EDX = g_dsp.r.wr[reg]
|
||||
// ESI = g_dsp.r.ix[reg]
|
||||
// ECX = temp
|
||||
// EDI = temp
|
||||
void DSPEmitter::decrease_addr_reg(int reg)
|
||||
{
|
||||
OpArg ar_reg;
|
||||
OpArg wr_reg;
|
||||
OpArg ix_reg;
|
||||
gpr.getReg(DSP_REG_WR0+reg,wr_reg);
|
||||
gpr.getReg(DSP_REG_IX0+reg,ix_reg);
|
||||
MOVZX(32, 16, EDX, wr_reg);
|
||||
MOVSX(32, 16, ESI, ix_reg);
|
||||
gpr.putReg(DSP_REG_WR0+reg);
|
||||
gpr.putReg(DSP_REG_IX0+reg);
|
||||
gpr.getReg(DSP_REG_AR0+reg,ar_reg);
|
||||
MOVZX(32, 16, EAX, ar_reg);
|
||||
|
||||
NOT(32, R(ESI)); //esi = ~ix
|
||||
|
||||
//u32 nar = ar - ix; (ar + ~ix + 1)
|
||||
LEA(32, EDI, MComplex(EAX, ESI, 1, 1));
|
||||
|
||||
//u32 dar = (nar ^ ar ^ ~ix) & ((wr | 1) << 1);
|
||||
//eax = dar
|
||||
XOR(32, R(EAX), R(ESI));
|
||||
XOR(32, R(EAX), R(EDI));
|
||||
LEA(32, ECX, MComplex(EDX, EDX, 1, 0));
|
||||
OR(32, R(ECX), Imm8(2));
|
||||
AND(32, R(EAX), R(ECX));
|
||||
|
||||
//if ((u32)ix > 0xFFFF8000) ==> (~ix < 0x00007FFF)
|
||||
CMP(32, R(ESI), Imm32(0x00007FFF));
|
||||
FixupBranch positive = J_CC(CC_AE);
|
||||
//if (dar > wr)
|
||||
CMP(32, R(EAX), R(EDX));
|
||||
FixupBranch done = J_CC(CC_BE);
|
||||
//nar -= wr + 1;
|
||||
SUB(16, R(DI), R(DX));
|
||||
SUB(16, R(DI), Imm8(1));
|
||||
FixupBranch done2 = J();
|
||||
|
||||
//else
|
||||
SetJumpTarget(positive);
|
||||
//if ((((nar + wr + 1) ^ nar) & dar) <= wr)
|
||||
LEA(32, ECX, MComplex(EDI, EDX, 1, 1));
|
||||
XOR(32, R(ECX), R(EDI));
|
||||
AND(32, R(ECX), R(EAX));
|
||||
CMP(32, R(ECX), R(EDX));
|
||||
FixupBranch done3 = J_CC(CC_A);
|
||||
//nar += wr + 1;
|
||||
LEA(32, EDI, MComplex(EDI, EDX, 1, 1));
|
||||
|
||||
SetJumpTarget(done);
|
||||
SetJumpTarget(done2);
|
||||
SetJumpTarget(done3);
|
||||
//return nar
|
||||
|
||||
MOV(16, ar_reg, R(DI));
|
||||
gpr.putReg(DSP_REG_AR0+reg);
|
||||
}
|
||||
|
||||
|
||||
// EAX - destination address
|
||||
// ECX - value
|
||||
// ESI - Base of dram
|
||||
void DSPEmitter::dmem_write()
|
||||
{
|
||||
// if (saddr == 0)
|
||||
CMP(16, R(EAX), Imm16(0x0fff));
|
||||
FixupBranch ifx = J_CC(CC_A);
|
||||
|
||||
// g_dsp.dram[addr & DSP_DRAM_MASK] = val;
|
||||
AND(16, R(EAX), Imm16(DSP_DRAM_MASK));
|
||||
#ifdef _M_X64
|
||||
MOV(64, R(ESI), ImmPtr(g_dsp.dram));
|
||||
#else
|
||||
MOV(32, R(ESI), ImmPtr(g_dsp.dram));
|
||||
#endif
|
||||
MOV(16, MComplex(ESI, EAX, 2, 0), R(ECX));
|
||||
|
||||
FixupBranch end = J();
|
||||
// else if (saddr == 0xf)
|
||||
SetJumpTarget(ifx);
|
||||
// Does it mean gdsp_ifx_write needs u32 rather than u16?
|
||||
DSPJitRegCache c(gpr);
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunctionRR((void *)gdsp_ifx_write, EAX, ECX);
|
||||
LoadDSPRegs();
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(end);
|
||||
}
|
||||
|
||||
// ECX - value
|
||||
void DSPEmitter::dmem_write_imm(u16 address)
|
||||
{
|
||||
switch (address >> 12)
|
||||
{
|
||||
case 0x0: // 0xxx DRAM
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, M(&g_dsp.dram[address & DSP_DRAM_MASK]), R(ECX));
|
||||
#else
|
||||
MOV(64, R(RDX), ImmPtr(g_dsp.dram));
|
||||
MOV(16, MDisp(RDX, (address & DSP_DRAM_MASK)*2), R(ECX));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0xf: // Fxxx HW regs
|
||||
MOV(16, R(EAX), Imm16(address));
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunctionRR((void *)gdsp_ifx_write, EAX, ECX);
|
||||
LoadDSPRegs();
|
||||
break;
|
||||
|
||||
default: // Unmapped/non-existing memory
|
||||
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Write to UNKNOWN (%04x) memory",
|
||||
g_dsp.pc, address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// In: ECX - the address to read
|
||||
// Out: EAX - the result of the read (used by caller)
|
||||
// ESI - Base
|
||||
void DSPEmitter::imem_read()
|
||||
{
|
||||
// if (addr == 0)
|
||||
CMP(16, R(ECX), Imm16(0x0fff));
|
||||
FixupBranch irom = J_CC(CC_A);
|
||||
// return g_dsp.iram[addr & DSP_IRAM_MASK];
|
||||
AND(16, R(ECX), Imm16(DSP_IRAM_MASK));
|
||||
#ifdef _M_X64
|
||||
MOV(64, R(ESI), ImmPtr(g_dsp.iram));
|
||||
#else
|
||||
MOV(32, R(ESI), ImmPtr(g_dsp.iram));
|
||||
#endif
|
||||
MOV(16, R(EAX), MComplex(ESI, ECX, 2, 0));
|
||||
|
||||
FixupBranch end = J();
|
||||
SetJumpTarget(irom);
|
||||
// else if (addr == 0x8)
|
||||
// return g_dsp.irom[addr & DSP_IROM_MASK];
|
||||
AND(16, R(ECX), Imm16(DSP_IROM_MASK));
|
||||
#ifdef _M_X64
|
||||
MOV(64, R(ESI), ImmPtr(g_dsp.irom));
|
||||
#else
|
||||
MOV(32, R(ESI), ImmPtr(g_dsp.irom));
|
||||
#endif
|
||||
MOV(16, R(EAX), MComplex(ESI, ECX, 2, 0));
|
||||
|
||||
SetJumpTarget(end);
|
||||
}
|
||||
|
||||
// In: ECX - the address to read
|
||||
// Out: EAX - the result of the read (used by caller)
|
||||
// ESI - Base
|
||||
// Trashes R11 on gdsp_ifx_read
|
||||
void DSPEmitter::dmem_read()
|
||||
{
|
||||
// if (saddr == 0)
|
||||
CMP(16, R(ECX), Imm16(0x0fff));
|
||||
FixupBranch dram = J_CC(CC_A);
|
||||
// return g_dsp.dram[addr & DSP_DRAM_MASK];
|
||||
#ifdef _M_X64
|
||||
AND(16, R(ECX), Imm16(DSP_DRAM_MASK));
|
||||
MOVZX(64, 16, RCX, R(RCX));
|
||||
MOV(64, R(ESI), ImmPtr(g_dsp.dram));
|
||||
#else
|
||||
AND(32, R(ECX), Imm32(DSP_DRAM_MASK));
|
||||
MOV(32, R(ESI), ImmPtr(g_dsp.dram));
|
||||
#endif
|
||||
MOV(16, R(EAX), MComplex(ESI, ECX, 2, 0));
|
||||
|
||||
FixupBranch end = J();
|
||||
SetJumpTarget(dram);
|
||||
// else if (saddr == 0x1)
|
||||
CMP(16, R(ECX), Imm16(0x1fff));
|
||||
FixupBranch ifx = J_CC(CC_A);
|
||||
// return g_dsp.coef[addr & DSP_COEF_MASK];
|
||||
#ifdef _M_X64
|
||||
AND(16, R(ECX), Imm16(DSP_COEF_MASK));
|
||||
MOVZX(64, 16, RCX, R(RCX));
|
||||
MOV(64, R(ESI), ImmPtr(g_dsp.coef));
|
||||
#else
|
||||
AND(32, R(ECX), Imm32(DSP_COEF_MASK));
|
||||
MOV(32, R(ESI), ImmPtr(g_dsp.coef));
|
||||
#endif
|
||||
MOV(16, R(EAX), MComplex(ESI, ECX, 2, 0));
|
||||
|
||||
FixupBranch end2 = J();
|
||||
SetJumpTarget(ifx);
|
||||
// else if (saddr == 0xf)
|
||||
// return gdsp_ifx_read(addr);
|
||||
DSPJitRegCache c(gpr);
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunctionR((void *)gdsp_ifx_read, ECX);
|
||||
LoadDSPRegs();
|
||||
gpr.flushRegs(c);
|
||||
SetJumpTarget(end);
|
||||
SetJumpTarget(end2);
|
||||
}
|
||||
|
||||
void DSPEmitter::dmem_read_imm(u16 address)
|
||||
{
|
||||
switch (address >> 12)
|
||||
{
|
||||
case 0x0: // 0xxx DRAM
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, R(EAX), M(&g_dsp.dram[address & DSP_DRAM_MASK]));
|
||||
#else
|
||||
MOV(64, R(RDX), ImmPtr(g_dsp.dram));
|
||||
MOV(16, R(EAX), MDisp(RDX, (address & DSP_DRAM_MASK)*2));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0x1: // 1xxx COEF
|
||||
#ifdef _M_IX86 // All32
|
||||
MOV(16, R(EAX), Imm16(g_dsp.coef[address & DSP_COEF_MASK]));
|
||||
#else
|
||||
MOV(64, R(RDX), ImmPtr(g_dsp.coef));
|
||||
MOV(16, R(EAX), MDisp(RDX, (address & DSP_COEF_MASK)*2));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0xf: // Fxxx HW regs
|
||||
SaveDSPRegs();
|
||||
ABI_CallFunctionC16((void *)gdsp_ifx_read, address);
|
||||
LoadDSPRegs();
|
||||
break;
|
||||
|
||||
default: // Unmapped/non-existing memory
|
||||
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Read from UNKNOWN (%04x) memory",
|
||||
g_dsp.pc, address);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns s64 in RAX
|
||||
void DSPEmitter::get_long_prod(X64Reg long_prod)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
//s64 val = (s8)(u8)g_dsp.r[DSP_REG_PRODH];
|
||||
OpArg reg;
|
||||
gpr.getReg(DSP_REG_PROD_64, reg);
|
||||
X64Reg tmp;
|
||||
gpr.getFreeXReg(tmp);
|
||||
MOV(64, R(long_prod), reg);
|
||||
MOV(64, R(tmp), R(long_prod));
|
||||
SHL(64, R(long_prod), Imm8(64-40));//sign extend
|
||||
SAR(64, R(long_prod), Imm8(64-40));
|
||||
SHR(64, R(tmp), Imm8(48));
|
||||
SHL(64, R(tmp), Imm8(16));
|
||||
ADD(64, R(long_prod), R(tmp));
|
||||
gpr.putXReg(tmp);
|
||||
gpr.putReg(DSP_REG_PROD_64, false);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns s64 in RAX
|
||||
// Clobbers RSI
|
||||
void DSPEmitter::get_long_prod_round_prodl(X64Reg long_prod)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
//s64 prod = dsp_get_long_prod();
|
||||
get_long_prod(long_prod);
|
||||
|
||||
//if (prod & 0x10000) prod = (prod + 0x8000) & ~0xffff;
|
||||
TEST(32, R(long_prod), Imm32(0x10000));
|
||||
FixupBranch jump = J_CC(CC_Z);
|
||||
ADD(64, R(long_prod), Imm32(0x8000));
|
||||
MOV(64, R(ESI), Imm64(~0xffff));
|
||||
AND(64, R(long_prod), R(RSI));
|
||||
FixupBranch _ret = J();
|
||||
//else prod = (prod + 0x7fff) & ~0xffff;
|
||||
SetJumpTarget(jump);
|
||||
ADD(64, R(long_prod), Imm32(0x7fff));
|
||||
MOV(64, R(RSI), Imm64(~0xffff));
|
||||
AND(64, R(long_prod), R(RSI));
|
||||
SetJumpTarget(_ret);
|
||||
//return prod;
|
||||
#endif
|
||||
}
|
||||
|
||||
// For accurate emulation, this is wrong - but the real prod registers behave
|
||||
// in completely bizarre ways. Probably not meaningful to emulate them accurately.
|
||||
// In: RAX = s64 val
|
||||
void DSPEmitter::set_long_prod()
|
||||
{
|
||||
#ifdef _M_X64
|
||||
OpArg reg;
|
||||
gpr.getReg(DSP_REG_PROD_64, reg, false);
|
||||
X64Reg tmp;
|
||||
gpr.getFreeXReg(tmp);
|
||||
|
||||
MOV(64, R(tmp), Imm64(0x000000ffffffffffULL));
|
||||
AND(64, R(RAX), R(tmp));
|
||||
// g_dsp.r[DSP_REG_PRODL] = (u16)val;
|
||||
MOV(64, reg, R(RAX));
|
||||
|
||||
gpr.putXReg(tmp);
|
||||
gpr.putReg(DSP_REG_PROD_64, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns s64 in RAX
|
||||
// Clobbers RSI
|
||||
void DSPEmitter::round_long_acc(X64Reg long_acc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
//if (prod & 0x10000) prod = (prod + 0x8000) & ~0xffff;
|
||||
TEST(32, R(long_acc), Imm32(0x10000));
|
||||
FixupBranch jump = J_CC(CC_Z);
|
||||
ADD(64, R(long_acc), Imm32(0x8000));
|
||||
MOV(64, R(ESI), Imm64(~0xffff));
|
||||
AND(64, R(long_acc), R(RSI));
|
||||
FixupBranch _ret = J();
|
||||
//else prod = (prod + 0x7fff) & ~0xffff;
|
||||
SetJumpTarget(jump);
|
||||
ADD(64, R(long_acc), Imm32(0x7fff));
|
||||
MOV(64, R(RSI), Imm64(~0xffff));
|
||||
AND(64, R(long_acc), R(RSI));
|
||||
SetJumpTarget(_ret);
|
||||
//return prod;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns s64 in acc
|
||||
void DSPEmitter::get_long_acc(int _reg, X64Reg acc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
OpArg reg;
|
||||
gpr.getReg(DSP_REG_ACC0_64+_reg, reg);
|
||||
MOV(64, R(acc), reg);
|
||||
gpr.putReg(DSP_REG_ACC0_64+_reg, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
// In: acc = s64 val
|
||||
void DSPEmitter::set_long_acc(int _reg, X64Reg acc)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
OpArg reg;
|
||||
gpr.getReg(DSP_REG_ACC0_64+_reg, reg, false);
|
||||
MOV(64, reg, R(acc));
|
||||
gpr.putReg(DSP_REG_ACC0_64+_reg);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns s16 in AX
|
||||
void DSPEmitter::get_acc_l(int _reg, X64Reg acl, bool sign)
|
||||
{
|
||||
// return g_dsp.r[DSP_REG_ACM0 + _reg];
|
||||
gpr.readReg(_reg+DSP_REG_ACL0, acl, sign?SIGN:ZERO);
|
||||
}
|
||||
|
||||
void DSPEmitter::set_acc_l(int _reg, OpArg arg)
|
||||
{
|
||||
// return g_dsp.r[DSP_REG_ACM0 + _reg];
|
||||
gpr.writeReg(_reg+DSP_REG_ACL0,arg);
|
||||
}
|
||||
|
||||
// Returns s16 in AX
|
||||
void DSPEmitter::get_acc_m(int _reg, X64Reg acm, bool sign)
|
||||
{
|
||||
// return g_dsp.r[DSP_REG_ACM0 + _reg];
|
||||
gpr.readReg(_reg+DSP_REG_ACM0, acm, sign?SIGN:ZERO);
|
||||
}
|
||||
|
||||
// In: s16 in AX
|
||||
void DSPEmitter::set_acc_m(int _reg, OpArg arg)
|
||||
{
|
||||
// return g_dsp.r.ac[_reg].m;
|
||||
gpr.writeReg(_reg+DSP_REG_ACM0,arg);
|
||||
}
|
||||
|
||||
// Returns s16 in AX
|
||||
void DSPEmitter::get_acc_h(int _reg, X64Reg ach, bool sign)
|
||||
{
|
||||
// return g_dsp.r.ac[_reg].h;
|
||||
gpr.readReg(_reg+DSP_REG_ACH0, ach, sign?SIGN:ZERO);
|
||||
}
|
||||
|
||||
// In: s16 in AX
|
||||
void DSPEmitter::set_acc_h(int _reg, OpArg arg)
|
||||
{
|
||||
// return g_dsp.r[DSP_REG_ACM0 + _reg];
|
||||
gpr.writeReg(_reg+DSP_REG_ACH0,arg);
|
||||
}
|
||||
|
||||
// Returns u32 in EAX
|
||||
void DSPEmitter::get_long_acx(int _reg, X64Reg acx)
|
||||
{
|
||||
// return ((u32)g_dsp.r[DSP_REG_AXH0 + _reg] << 16) | g_dsp.r[DSP_REG_AXL0 + _reg];
|
||||
gpr.readReg(_reg+DSP_REG_AX0_32, acx, SIGN);
|
||||
}
|
||||
|
||||
// Returns s16 in EAX
|
||||
void DSPEmitter::get_ax_l(int _reg, X64Reg axl)
|
||||
{
|
||||
// return (s16)g_dsp.r[DSP_REG_AXL0 + _reg];
|
||||
gpr.readReg(_reg+DSP_REG_AXL0, axl, SIGN);
|
||||
}
|
||||
|
||||
// Returns s16 in EAX
|
||||
void DSPEmitter::get_ax_h(int _reg, X64Reg axh)
|
||||
{
|
||||
// return (s16)g_dsp.r[DSP_REG_AXH0 + _reg];
|
||||
gpr.readReg(_reg+DSP_REG_AXH0, axh, SIGN);
|
||||
}
|
||||
|
||||
void DSPEmitter::LoadDSPRegs()
|
||||
{
|
||||
#ifdef _M_X64
|
||||
MOV(64, R(R11), ImmPtr(&g_dsp.r));
|
||||
#endif
|
||||
|
||||
// Load DSP register state here...
|
||||
gpr.loadStaticRegs();
|
||||
}
|
||||
|
||||
void DSPEmitter::SaveDSPRegs()
|
||||
{
|
||||
// Save DSP register state here...
|
||||
gpr.saveStaticRegs();
|
||||
}
|
||||
21
Source/Core/Core/Src/DSP/Jit/DSPJitUtil.h
Normal file
21
Source/Core/Core/Src/DSP/Jit/DSPJitUtil.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2010 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef __DSPJITUTIL_H__
|
||||
#define __DSPJITUTIL_H__
|
||||
|
||||
#endif /*__DSPJITUTIL_H__*/
|
||||
85
Source/Core/Core/Src/DSP/LabelMap.cpp
Normal file
85
Source/Core/Core/Src/DSP/LabelMap.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "LabelMap.h"
|
||||
#include "DSPTables.h"
|
||||
|
||||
LabelMap::LabelMap()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void LabelMap::RegisterDefaults()
|
||||
{
|
||||
for (int i = 0; i < 0x24; i++)
|
||||
{
|
||||
if (regnames[i].name)
|
||||
RegisterLabel(regnames[i].name, regnames[i].addr);
|
||||
}
|
||||
for (int i = 0; i < (int)pdlabels_size; i++)
|
||||
{
|
||||
if (pdlabels[i].name)
|
||||
RegisterLabel(pdlabels[i].name, pdlabels[i].addr);
|
||||
}
|
||||
}
|
||||
|
||||
void LabelMap::RegisterLabel(const std::string &label, u16 lval, LabelType type)
|
||||
{
|
||||
u16 old_value;
|
||||
if (GetLabelValue(label, &old_value) && old_value != lval)
|
||||
{
|
||||
printf("WARNING: Redefined label %s to %04x - old value %04x\n",
|
||||
label.c_str(), lval, old_value);
|
||||
DeleteLabel(label);
|
||||
}
|
||||
labels.push_back(label_t(label, lval, type));
|
||||
}
|
||||
|
||||
void LabelMap::DeleteLabel(const std::string &label)
|
||||
{
|
||||
for (std::vector<label_t>::iterator iter = labels.begin();
|
||||
iter != labels.end(); ++iter)
|
||||
{
|
||||
if (!label.compare(iter->name))
|
||||
{
|
||||
labels.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LabelMap::GetLabelValue(const std::string &label, u16 *value, LabelType type) const
|
||||
{
|
||||
for (u32 i = 0; i < labels.size(); i++)
|
||||
{
|
||||
if (!label.compare(labels[i].name))
|
||||
{
|
||||
if (type & labels[i].type) {
|
||||
*value = labels[i].addr;
|
||||
return true;
|
||||
} else {
|
||||
printf("WARNING: Wrong label type requested. %s\n", label.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LabelMap::Clear()
|
||||
{
|
||||
labels.clear();
|
||||
}
|
||||
55
Source/Core/Core/Src/DSP/LabelMap.h
Normal file
55
Source/Core/Core/Src/DSP/LabelMap.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _LABELMAP_H
|
||||
#define _LABELMAP_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
enum LabelType
|
||||
{
|
||||
LABEL_IADDR = 1, // Jump addresses, etc
|
||||
LABEL_DADDR = 2, // Data addresses, etc
|
||||
LABEL_VALUE = 4,
|
||||
LABEL_ANY = 0xFF,
|
||||
};
|
||||
|
||||
class LabelMap
|
||||
{
|
||||
struct label_t
|
||||
{
|
||||
label_t(const std::string &lbl, s32 address, LabelType ltype) : name(lbl), addr(address), type(ltype) {}
|
||||
std::string name;
|
||||
s32 addr;
|
||||
LabelType type;
|
||||
};
|
||||
std::vector<label_t> labels;
|
||||
|
||||
public:
|
||||
LabelMap();
|
||||
~LabelMap() { }
|
||||
void RegisterDefaults();
|
||||
void RegisterLabel(const std::string &label, u16 lval, LabelType type = LABEL_VALUE);
|
||||
void DeleteLabel(const std::string &label);
|
||||
bool GetLabelValue(const std::string &label, u16 *value, LabelType type = LABEL_ANY) const;
|
||||
void Clear();
|
||||
};
|
||||
|
||||
#endif // _LABELMAP_H
|
||||
1020
Source/Core/Core/Src/DSP/assemble.cpp
Normal file
1020
Source/Core/Core/Src/DSP/assemble.cpp
Normal file
File diff suppressed because it is too large
Load Diff
139
Source/Core/Core/Src/DSP/assemble.h
Normal file
139
Source/Core/Core/Src/DSP/assemble.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*====================================================================
|
||||
|
||||
project: GameCube DSP Tool (gcdsp)
|
||||
created: 2005.03.04
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#ifndef _DSP_ASSEMBLE_H
|
||||
#define _DSP_ASSEMBLE_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "disassemble.h"
|
||||
#include "DSPTables.h"
|
||||
#include "LabelMap.h"
|
||||
|
||||
enum err_t
|
||||
{
|
||||
ERR_OK = 0,
|
||||
ERR_UNKNOWN,
|
||||
ERR_UNKNOWN_OPCODE,
|
||||
ERR_NOT_ENOUGH_PARAMETERS,
|
||||
ERR_TOO_MANY_PARAMETERS,
|
||||
ERR_WRONG_PARAMETER,
|
||||
ERR_EXPECTED_PARAM_STR,
|
||||
ERR_EXPECTED_PARAM_VAL,
|
||||
ERR_EXPECTED_PARAM_REG,
|
||||
ERR_EXPECTED_PARAM_MEM,
|
||||
ERR_EXPECTED_PARAM_IMM,
|
||||
ERR_INCORRECT_BIN,
|
||||
ERR_INCORRECT_HEX,
|
||||
ERR_INCORRECT_DEC,
|
||||
ERR_LABEL_EXISTS,
|
||||
ERR_UNKNOWN_LABEL,
|
||||
ERR_NO_MATCHING_BRACKETS,
|
||||
ERR_EXT_CANT_EXTEND_OPCODE,
|
||||
ERR_EXT_PAR_NOT_EXT,
|
||||
ERR_WRONG_PARAMETER_ACC,
|
||||
ERR_WRONG_PARAMETER_MID_ACC,
|
||||
ERR_INVALID_REGISTER,
|
||||
ERR_OUT_RANGE_NUMBER
|
||||
};
|
||||
|
||||
|
||||
// Unless you want labels to carry over between files, you probably
|
||||
// want to create a new DSPAssembler for every file you assemble.
|
||||
class DSPAssembler
|
||||
{
|
||||
public:
|
||||
DSPAssembler(const AssemblerSettings &settings);
|
||||
~DSPAssembler();
|
||||
|
||||
// line_numbers is optional (and not yet implemented). It'll receieve a list of ints,
|
||||
// one for each word of code, indicating the source assembler code line number it came from.
|
||||
|
||||
// If returns false, call GetErrorString to get some text to present to the user.
|
||||
bool Assemble(const char *text, std::vector<u16> &code, std::vector<int> *line_numbers = NULL);
|
||||
|
||||
std::string GetErrorString() const { return last_error_str; }
|
||||
err_t GetError() const { return last_error; }
|
||||
|
||||
private:
|
||||
struct param_t
|
||||
{
|
||||
u32 val;
|
||||
partype_t type;
|
||||
char *str;
|
||||
};
|
||||
|
||||
enum segment_t
|
||||
{
|
||||
SEGMENT_CODE = 0,
|
||||
SEGMENT_DATA,
|
||||
SEGMENT_OVERLAY,
|
||||
SEGMENT_MAX
|
||||
};
|
||||
|
||||
// Utility functions
|
||||
s32 ParseValue(const char *str);
|
||||
u32 ParseExpression(const char *ptr);
|
||||
|
||||
u32 GetParams(char *parstr, param_t *par);
|
||||
|
||||
void InitPass(int pass);
|
||||
bool AssembleFile(const char *fname, int pass);
|
||||
|
||||
void ShowError(err_t err_code, const char *extra_info = NULL);
|
||||
// void ShowWarning(err_t err_code, const char *extra_info = NULL);
|
||||
|
||||
char *FindBrackets(char *src, char *dst);
|
||||
const opc_t *FindOpcode(const char *opcode, u32 par_count, const opc_t * const opcod, int opcod_size);
|
||||
bool VerifyParams(const opc_t *opc, param_t *par, int count, bool ext = false);
|
||||
void BuildCode(const opc_t *opc, param_t *par, u32 par_count, u16 *outbuf);
|
||||
|
||||
char *gdg_buffer;
|
||||
|
||||
std::string include_dir;
|
||||
std::string cur_line;
|
||||
|
||||
u32 m_cur_addr;
|
||||
int m_totalSize;
|
||||
u8 m_cur_pass;
|
||||
|
||||
LabelMap labels;
|
||||
|
||||
u32 code_line;
|
||||
bool failed;
|
||||
std::string last_error_str;
|
||||
err_t last_error;
|
||||
|
||||
typedef std::map<std::string, std::string> AliasMap;
|
||||
AliasMap aliases;
|
||||
|
||||
segment_t cur_segment;
|
||||
u32 segment_addr[SEGMENT_MAX];
|
||||
int m_current_param;
|
||||
const AssemblerSettings settings_;
|
||||
};
|
||||
|
||||
#endif // _DSP_ASSEMBLE_H
|
||||
360
Source/Core/Core/Src/DSP/disassemble.cpp
Normal file
360
Source/Core/Core/Src/DSP/disassemble.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
/*====================================================================
|
||||
|
||||
filename: disassemble.cpp
|
||||
project: GameCube DSP Tool (gcdsp)
|
||||
created: 2005.03.04
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "FileUtil.h"
|
||||
#include "disassemble.h"
|
||||
#include "DSPTables.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PATH
|
||||
#include <Windows.h> // For MAX_PATH
|
||||
#endif
|
||||
|
||||
extern void nop(const UDSPInstruction opc);
|
||||
|
||||
DSPDisassembler::DSPDisassembler(const AssemblerSettings &settings)
|
||||
: settings_(settings)
|
||||
{
|
||||
}
|
||||
|
||||
DSPDisassembler::~DSPDisassembler()
|
||||
{
|
||||
// Some old code for logging unknown ops.
|
||||
char filename[MAX_PATH];
|
||||
sprintf(filename, "%sUnkOps.txt", File::GetUserPath(D_DUMPDSP_IDX));
|
||||
FILE *uo = fopen(filename, "w");
|
||||
if (!uo)
|
||||
return;
|
||||
|
||||
int count = 0;
|
||||
for (std::map<u16, int>::const_iterator iter = unk_opcodes.begin();
|
||||
iter != unk_opcodes.end(); ++iter)
|
||||
{
|
||||
if (iter->second > 0)
|
||||
{
|
||||
count++;
|
||||
fprintf(uo, "OP%04x\t%d", iter->first, iter->second);
|
||||
for (int j = 15; j >= 0; j--) // print op bits
|
||||
{
|
||||
if ((j & 0x3) == 3)
|
||||
fprintf(uo, "\tb");
|
||||
fprintf(uo, "%d", (iter->first >> j) & 0x1);
|
||||
}
|
||||
fprintf(uo, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(uo, "Unknown opcodes count: %d\n", count);
|
||||
fclose(uo);
|
||||
}
|
||||
|
||||
bool DSPDisassembler::Disassemble(int start_pc, const std::vector<u16> &code, int base_addr, std::string &text)
|
||||
{
|
||||
const char *tmp1 = "tmp1.bin";
|
||||
|
||||
// First we have to dump the code to a bin file.
|
||||
FILE *f = fopen(tmp1, "wb");
|
||||
fwrite(&code[0], 1, code.size() * 2, f);
|
||||
fclose(f);
|
||||
|
||||
// Run the two passes.
|
||||
return DisFile(tmp1, base_addr, 1, text) && DisFile(tmp1, base_addr, 2, text);
|
||||
}
|
||||
|
||||
char *DSPDisassembler::DisParams(const DSPOPCTemplate& opc, u16 op1, u16 op2, char *strbuf)
|
||||
{
|
||||
char *buf = strbuf;
|
||||
for (int j = 0; j < opc.param_count; j++)
|
||||
{
|
||||
if (j > 0)
|
||||
buf += sprintf(buf, ", ");
|
||||
|
||||
u32 val = (opc.params[j].loc >= 1) ? op2 : op1;
|
||||
val &= opc.params[j].mask;
|
||||
if (opc.params[j].lshift < 0)
|
||||
val = val << (-opc.params[j].lshift);
|
||||
else
|
||||
val = val >> opc.params[j].lshift;
|
||||
|
||||
u32 type = opc.params[j].type;
|
||||
if ((type & 0xff) == 0x10)
|
||||
type &= 0xff00;
|
||||
|
||||
if (type & P_REG)
|
||||
{
|
||||
// Check for _D parameter - if so flip.
|
||||
if ((type == P_ACC_D) || (type == P_ACCM_D)) // Used to be P_ACCM_D TODO verify
|
||||
val = (~val & 0x1) | ((type & P_REGS_MASK) >> 8);
|
||||
else
|
||||
val |= (type & P_REGS_MASK) >> 8;
|
||||
type &= ~P_REGS_MASK;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case P_REG:
|
||||
if (settings_.decode_registers)
|
||||
sprintf(buf, "$%s", pdregname(val));
|
||||
else
|
||||
sprintf(buf, "$%d", val);
|
||||
break;
|
||||
|
||||
case P_PRG:
|
||||
if (settings_.decode_registers)
|
||||
sprintf(buf, "@$%s", pdregname(val));
|
||||
else
|
||||
sprintf(buf, "@$%d", val);
|
||||
break;
|
||||
|
||||
case P_VAL:
|
||||
case P_ADDR_I:
|
||||
case P_ADDR_D:
|
||||
if (settings_.decode_names)
|
||||
{
|
||||
sprintf(buf, "%s", pdname(val));
|
||||
}
|
||||
else
|
||||
sprintf(buf, "0x%04x", val);
|
||||
break;
|
||||
|
||||
case P_IMM:
|
||||
if (opc.params[j].size != 2)
|
||||
{
|
||||
if (opc.params[j].mask == 0x003f) // LSL, LSR, ASL, ASR
|
||||
sprintf(buf, "#%d", (val & 0x20) ? (val | 0xFFFFFFC0) : val); // 6-bit sign extension
|
||||
else
|
||||
sprintf(buf, "#0x%02x", val);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(buf, "#0x%04x", val);
|
||||
}
|
||||
break;
|
||||
|
||||
case P_MEM:
|
||||
if (opc.params[j].size != 2)
|
||||
val = (u16)(s16)(s8)val;
|
||||
|
||||
if (settings_.decode_names)
|
||||
sprintf(buf, "@%s", pdname(val));
|
||||
else
|
||||
sprintf(buf, "@0x%04x", val);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(DSPLLE, "Unknown parameter type: %x", opc.params[j].type);
|
||||
break;
|
||||
}
|
||||
|
||||
buf += strlen(buf);
|
||||
}
|
||||
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static void MakeLowerCase(char *ptr)
|
||||
{
|
||||
int i = 0;
|
||||
while (ptr[i])
|
||||
{
|
||||
ptr[i] = tolower(ptr[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
bool DSPDisassembler::DisOpcode(const u16 *binbuf, int base_addr, int pass, u16 *pc, std::string &dest)
|
||||
{
|
||||
char buffer[256];
|
||||
char *buf = buffer;
|
||||
|
||||
// Start with 8 spaces, if there's no label.
|
||||
buf[0] = ' ';
|
||||
buf[1] = '\0';
|
||||
buf++;
|
||||
|
||||
if ((*pc & 0x7fff) >= 0x1000)
|
||||
{
|
||||
++pc;
|
||||
dest.append("; outside memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 op1 = binbuf[*pc & 0x0fff];
|
||||
|
||||
const DSPOPCTemplate *opc = NULL;
|
||||
const DSPOPCTemplate *opc_ext = NULL;
|
||||
|
||||
// find opcode
|
||||
for (int j = 0; j < opcodes_size; j++)
|
||||
{
|
||||
u16 mask = opcodes[j].opcode_mask;
|
||||
|
||||
if ((op1 & mask) == opcodes[j].opcode)
|
||||
{
|
||||
opc = &opcodes[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
const DSPOPCTemplate fake_op = {"CW", 0x0000, 0x0000, nop, NULL, 1, 1, {{P_VAL, 2, 0, 0, 0xffff}}, false};
|
||||
if (!opc)
|
||||
opc = &fake_op;
|
||||
|
||||
bool extended = false;
|
||||
bool only7bitext = false;
|
||||
|
||||
if (((opc->opcode >> 12) == 0x3) && (op1 & 0x007f)) {
|
||||
extended = true;
|
||||
only7bitext = true;
|
||||
}
|
||||
else if (((opc->opcode >> 12) > 0x3) && (op1 & 0x00ff))
|
||||
extended = true;
|
||||
else
|
||||
extended = false;
|
||||
|
||||
if (extended)
|
||||
{
|
||||
// opcode has an extension
|
||||
// find opcode
|
||||
for (int j = 0; j < opcodes_ext_size; j++)
|
||||
{
|
||||
if (only7bitext) {
|
||||
if (((op1 & 0x7f) & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode)
|
||||
{
|
||||
opc_ext = &opcodes_ext[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((op1 & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode)
|
||||
{
|
||||
opc_ext = &opcodes_ext[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printing
|
||||
|
||||
if (settings_.show_pc)
|
||||
buf += sprintf(buf, "%04x ", *pc);
|
||||
|
||||
u32 op2;
|
||||
|
||||
// Size 2 - the op has a large immediate.
|
||||
if (opc->size == 2)
|
||||
{
|
||||
op2 = binbuf[(*pc + 1) & 0x0fff];
|
||||
if (settings_.show_hex)
|
||||
buf += sprintf(buf, "%04x %04x ", op1, op2);
|
||||
}
|
||||
else
|
||||
{
|
||||
op2 = 0;
|
||||
if (settings_.show_hex)
|
||||
buf += sprintf(buf, "%04x ", op1);
|
||||
}
|
||||
|
||||
char opname[20];
|
||||
strcpy(opname, opc->name);
|
||||
if (settings_.lower_case_ops)
|
||||
MakeLowerCase(opname);
|
||||
char ext_buf[20];
|
||||
if (extended)
|
||||
sprintf(ext_buf, "%s%c%s", opname, settings_.ext_separator, opc_ext->name);
|
||||
else
|
||||
sprintf(ext_buf, "%s", opname);
|
||||
if (settings_.lower_case_ops)
|
||||
MakeLowerCase(ext_buf);
|
||||
|
||||
if (settings_.print_tabs)
|
||||
buf += sprintf(buf, "%s\t", ext_buf);
|
||||
else
|
||||
buf += sprintf(buf, "%-12s", ext_buf);
|
||||
|
||||
if (opc->param_count > 0)
|
||||
DisParams(*opc, op1, op2, buf);
|
||||
|
||||
buf += strlen(buf);
|
||||
|
||||
// Handle opcode extension.
|
||||
if (extended)
|
||||
{
|
||||
if (opc->param_count > 0)
|
||||
buf += sprintf(buf, " ");
|
||||
buf += sprintf(buf, ": ");
|
||||
if (opc_ext->param_count > 0)
|
||||
DisParams(*opc_ext, op1, op2, buf);
|
||||
|
||||
buf += strlen(buf);
|
||||
}
|
||||
|
||||
if (opc->opcode_mask == 0)
|
||||
{
|
||||
// unknown opcode
|
||||
unk_opcodes[op1]++;
|
||||
sprintf(buf, "\t\t; *** UNKNOWN OPCODE ***");
|
||||
}
|
||||
|
||||
if (extended)
|
||||
*pc += opc_ext->size;
|
||||
else
|
||||
*pc += opc->size;
|
||||
|
||||
if (pass == 2)
|
||||
dest.append(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSPDisassembler::DisFile(const char* name, int base_addr, int pass, std::string &output)
|
||||
{
|
||||
FILE* in = fopen(name, "rb");
|
||||
if (in == NULL)
|
||||
{
|
||||
printf("gd_dis_file: No input\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int size = (int)File::GetSize(in) & ~1;
|
||||
u16 *binbuf = new u16[size / 2];
|
||||
fread(binbuf, 1, size, in);
|
||||
fclose(in);
|
||||
|
||||
// Actually do the disassembly.
|
||||
for (u16 pc = 0; pc < (size / 2);)
|
||||
{
|
||||
DisOpcode(binbuf, base_addr, pass, &pc, output);
|
||||
if (pass == 2)
|
||||
output.append("\n");
|
||||
}
|
||||
delete [] binbuf;
|
||||
return true;
|
||||
}
|
||||
87
Source/Core/Core/Src/DSP/disassemble.h
Normal file
87
Source/Core/Core/Src/DSP/disassemble.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*====================================================================
|
||||
|
||||
project: GameCube DSP Tool (gcdsp)
|
||||
created: 2005.03.04
|
||||
mail: duddie@walla.com
|
||||
|
||||
Copyright (c) 2005 Duddie
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
====================================================================*/
|
||||
|
||||
#ifndef _DSP_DISASSEMBLE_H
|
||||
#define _DSP_DISASSEMBLE_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
#include "DSPTables.h"
|
||||
#include "LabelMap.h"
|
||||
|
||||
struct AssemblerSettings
|
||||
{
|
||||
AssemblerSettings()
|
||||
: print_tabs(false),
|
||||
show_hex(false),
|
||||
show_pc(false),
|
||||
force(false),
|
||||
decode_names(true),
|
||||
decode_registers(true),
|
||||
ext_separator('\''),
|
||||
lower_case_ops(true),
|
||||
pc(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool print_tabs;
|
||||
bool show_hex;
|
||||
bool show_pc;
|
||||
bool force;
|
||||
bool decode_names;
|
||||
bool decode_registers;
|
||||
char ext_separator;
|
||||
bool lower_case_ops;
|
||||
|
||||
u16 pc;
|
||||
};
|
||||
|
||||
class DSPDisassembler
|
||||
{
|
||||
public:
|
||||
DSPDisassembler(const AssemblerSettings &settings);
|
||||
~DSPDisassembler();
|
||||
|
||||
bool Disassemble(int start_pc, const std::vector<u16> &code, int base_addr, std::string &text);
|
||||
|
||||
// Warning - this one is trickier to use right.
|
||||
// Use pass == 2 if you're just using it by itself.
|
||||
bool DisOpcode(const u16 *binbuf, int base_addr, int pass, u16 *pc, std::string &dest);
|
||||
|
||||
private:
|
||||
// Moves PC forward and writes the result to dest.
|
||||
bool DisFile(const char* name, int base_addr, int pass, std::string &output);
|
||||
|
||||
char* DisParams(const DSPOPCTemplate& opc, u16 op1, u16 op2, char* strbuf);
|
||||
std::map<u16, int> unk_opcodes;
|
||||
|
||||
const AssemblerSettings settings_;
|
||||
|
||||
LabelMap labels;
|
||||
};
|
||||
|
||||
#endif // _DSP_DISASSEMBLE_H
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
|
||||
#include "DSPDebugInterface.h"
|
||||
|
||||
#include "DSPCore.h"
|
||||
#include "disassemble.h"
|
||||
#include "DSP/DSPCore.h"
|
||||
#include "DSP/disassemble.h"
|
||||
|
||||
#include "DSPSymbols.h"
|
||||
#include "DSPMemoryMap.h"
|
||||
#include "DSP/DSPMemoryMap.h"
|
||||
|
||||
void DSPDebugInterface::disasm(unsigned int address, char *dest, int max_size)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "Hash.h"
|
||||
#include "DSPHost.h"
|
||||
#include "DSP/DSPHost.h"
|
||||
#include "DSPSymbols.h"
|
||||
#include "DSPLLETools.h"
|
||||
#include "../DSP.h"
|
||||
|
||||
@@ -26,16 +26,16 @@
|
||||
#include "IniFile.h"
|
||||
|
||||
#include "DSPLLEGlobals.h" // Local
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSPHWInterface.h"
|
||||
#include "disassemble.h"
|
||||
#include "DSP/DSPInterpreter.h"
|
||||
#include "DSP/DSPHWInterface.h"
|
||||
#include "DSP/disassemble.h"
|
||||
#include "DSPSymbols.h"
|
||||
|
||||
#include "AudioCommon.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
#include "DSPTables.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSP/DSPTables.h"
|
||||
#include "DSP/DSPCore.h"
|
||||
#include "DSPLLE.h"
|
||||
#include "../Memmap.h"
|
||||
#include "../AudioInterface.h"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "Common.h" // for Common::swap
|
||||
#include "DSPCore.h"
|
||||
#include "DSP/DSPCore.h"
|
||||
#include "DSPLLEGlobals.h"
|
||||
|
||||
#if PROFILE
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
#include "DSPLLEGlobals.h"
|
||||
|
||||
#include "FileUtil.h"
|
||||
#include "DSPCore.h"
|
||||
#include "DSPCodeUtil.h"
|
||||
#include "DSP/DSPCore.h"
|
||||
#include "DSP/DSPCodeUtil.h"
|
||||
#include "DSPLLETools.h"
|
||||
#include "disassemble.h"
|
||||
#include "DSPInterpreter.h"
|
||||
#include "DSP/disassemble.h"
|
||||
#include "DSP/DSPInterpreter.h"
|
||||
|
||||
bool DumpDSPCode(const u8 *code_be, int size_in_bytes, u32 crc)
|
||||
{
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
#include "Common.h"
|
||||
#include "StringUtil.h"
|
||||
|
||||
#include "DSPCore.h"
|
||||
#include "DSP/DSPCore.h"
|
||||
#include "DSPSymbols.h"
|
||||
#include "disassemble.h"
|
||||
#include "DSP/disassemble.h"
|
||||
|
||||
namespace DSPSymbols {
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import sys
|
||||
files = [
|
||||
"ActionReplay.cpp",
|
||||
"ARDecrypt.cpp",
|
||||
"GeckoCode.cpp",
|
||||
"GeckoCodeConfig.cpp",
|
||||
"ConfigManager.cpp",
|
||||
"Console.cpp",
|
||||
"Core.cpp",
|
||||
@@ -32,6 +30,37 @@ files = [
|
||||
"Debugger/Debugger_SymbolMap.cpp",
|
||||
"Debugger/Dump.cpp",
|
||||
"Debugger/PPCDebugInterface.cpp",
|
||||
"DSP/assemble.cpp",
|
||||
"DSP/disassemble.cpp",
|
||||
"DSP/DSPAccelerator.cpp",
|
||||
"DSP/DSPIntCCUtil.cpp",
|
||||
"DSP/DSPIntExtOps.cpp",
|
||||
"DSP/DSPHWInterface.cpp",
|
||||
"DSP/DSPMemoryMap.cpp",
|
||||
"DSP/DSPStacks.cpp",
|
||||
"DSP/DSPAnalyzer.cpp",
|
||||
"DSP/DspIntArithmetic.cpp",
|
||||
"DSP/DspIntBranch.cpp",
|
||||
"DSP/DspIntLoadStore.cpp",
|
||||
"DSP/DspIntMisc.cpp",
|
||||
"DSP/DspIntMultiplier.cpp",
|
||||
"DSP/DSPEmitter.cpp",
|
||||
"DSP/DSPCodeUtil.cpp",
|
||||
"DSP/LabelMap.cpp",
|
||||
"DSP/DSPInterpreter.cpp",
|
||||
"DSP/DSPCore.cpp",
|
||||
"DSP/DSPTables.cpp",
|
||||
"DSP/Jit/DSPJitExtOps.cpp",
|
||||
"DSP/Jit/DSPJitBranch.cpp",
|
||||
"DSP/Jit/DSPJitUtil.cpp",
|
||||
"DSP/Jit/DSPJitCCUtil.cpp",
|
||||
"DSP/Jit/DSPJitArithmetic.cpp",
|
||||
"DSP/Jit/DSPJitLoadStore.cpp",
|
||||
"DSP/Jit/DSPJitMultiplier.cpp",
|
||||
"DSP/Jit/DSPJitMisc.cpp",
|
||||
"DSP/Jit/DSPJitRegCache.cpp",
|
||||
"GeckoCode.cpp",
|
||||
"GeckoCodeConfig.cpp",
|
||||
"HLE/HLE.cpp",
|
||||
"HLE/HLE_Misc.cpp",
|
||||
"HLE/HLE_OS.cpp",
|
||||
@@ -165,7 +194,7 @@ if sys.platform == 'win32':
|
||||
elif sys.platform == 'darwin':
|
||||
files += [ "HW/BBA-TAP/TAP_Apple.cpp", "HW/WiimoteReal/IOdarwin.mm" ]
|
||||
elif sys.platform == 'linux2' and env['HAVE_BLUEZ']:
|
||||
files += [ "HW/BBA-TAP/TAP_Unix.cpp", "HW/WiimoteReal/IONix.cpp" ]
|
||||
files += [ "HW/BBA-TAP/TAP_Unix.cpp", "HW/WiimoteReal/IODummy.cpp" ]
|
||||
else:
|
||||
files += [ "HW/BBA-TAP/TAP_Unix.cpp", "HW/WiimoteReal/IODummy.cpp" ]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user