mirror of
				https://github.com/N64Recomp/N64Recomp.git
				synced 2025-10-30 08:02:11 +00:00 
			
		
		
		
	Implemented initial set of instructions and ignored functions
This commit is contained in:
		
							parent
							
								
									4b1dc14019
								
							
						
					
					
						commit
						8a0f0da0cc
					
				
					 9 changed files with 1204 additions and 15 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -4,6 +4,9 @@ | ||||||
| # Input elf files | # Input elf files | ||||||
| *.elf | *.elf | ||||||
| 
 | 
 | ||||||
|  | # Output C files | ||||||
|  | out/ | ||||||
|  | 
 | ||||||
| # Linux build output | # Linux build output | ||||||
| build/ | build/ | ||||||
| *.o | *.o | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ | ||||||
|     <VCProjectVersion>16.0</VCProjectVersion> |     <VCProjectVersion>16.0</VCProjectVersion> | ||||||
|     <ProjectGuid>{23C26E84-DC01-43A6-B11B-0B4A2D79A5DD}</ProjectGuid> |     <ProjectGuid>{23C26E84-DC01-43A6-B11B-0B4A2D79A5DD}</ProjectGuid> | ||||||
|     <Keyword>Win32Proj</Keyword> |     <Keyword>Win32Proj</Keyword> | ||||||
|  |     <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> |   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> | ||||||
|  | @ -75,8 +76,9 @@ | ||||||
|       <WarningLevel>Level3</WarningLevel> |       <WarningLevel>Level3</WarningLevel> | ||||||
|       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> |       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> | ||||||
|       <Optimization>Disabled</Optimization> |       <Optimization>Disabled</Optimization> | ||||||
|       <LanguageStandard>stdcpp17</LanguageStandard> |       <LanguageStandard>stdcpp20</LanguageStandard> | ||||||
|       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||||
|  |       <OpenMPSupport>true</OpenMPSupport> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|     <Link> |     <Link> | ||||||
|       <TargetMachine>MachineX86</TargetMachine> |       <TargetMachine>MachineX86</TargetMachine> | ||||||
|  | @ -91,8 +93,9 @@ | ||||||
|       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> |       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> | ||||||
|       <WarningLevel>Level3</WarningLevel> |       <WarningLevel>Level3</WarningLevel> | ||||||
|       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> |       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> | ||||||
|       <LanguageStandard>stdcpp17</LanguageStandard> |       <LanguageStandard>stdcpp20</LanguageStandard> | ||||||
|       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||||
|  |       <OpenMPSupport>true</OpenMPSupport> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|     <Link> |     <Link> | ||||||
|       <TargetMachine>MachineX86</TargetMachine> |       <TargetMachine>MachineX86</TargetMachine> | ||||||
|  | @ -105,8 +108,9 @@ | ||||||
|   </ItemDefinitionGroup> |   </ItemDefinitionGroup> | ||||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> |   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||||
|     <ClCompile> |     <ClCompile> | ||||||
|       <LanguageStandard>stdcpp17</LanguageStandard> |       <LanguageStandard>stdcpp20</LanguageStandard> | ||||||
|       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||||
|  |       <OpenMPSupport>true</OpenMPSupport> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|     <Link> |     <Link> | ||||||
|       <SubSystem>Console</SubSystem> |       <SubSystem>Console</SubSystem> | ||||||
|  | @ -115,8 +119,9 @@ | ||||||
|   </ItemDefinitionGroup> |   </ItemDefinitionGroup> | ||||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> |   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||||
|     <ClCompile> |     <ClCompile> | ||||||
|       <LanguageStandard>stdcpp17</LanguageStandard> |       <LanguageStandard>stdcpp20</LanguageStandard> | ||||||
|       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||||
|  |       <OpenMPSupport>true</OpenMPSupport> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|     <Link> |     <Link> | ||||||
|       <SubSystem>Console</SubSystem> |       <SubSystem>Console</SubSystem> | ||||||
|  | @ -133,8 +138,10 @@ | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClCompile Include="src\main.cpp" /> |     <ClCompile Include="src\main.cpp" /> | ||||||
|  |     <ClCompile Include="src\recompilation.cpp" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|  |     <ClInclude Include="include\recomp_port.h" /> | ||||||
|     <ClInclude Include="lib\ELFIO\elfio\elfio.hpp" /> |     <ClInclude Include="lib\ELFIO\elfio\elfio.hpp" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||||||
|  |  | ||||||
|  | @ -18,10 +18,16 @@ | ||||||
|     <ClCompile Include="src\main.cpp"> |     <ClCompile Include="src\main.cpp"> | ||||||
|       <Filter>Source Files</Filter> |       <Filter>Source Files</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="src\recompilation.cpp"> | ||||||
|  |       <Filter>Source Files</Filter> | ||||||
|  |     </ClCompile> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClInclude Include="lib\ELFIO\elfio\elfio.hpp"> |     <ClInclude Include="lib\ELFIO\elfio\elfio.hpp"> | ||||||
|       <Filter>Header Files</Filter> |       <Filter>Header Files</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  |     <ClInclude Include="include\recomp_port.h"> | ||||||
|  |       <Filter>Header Files</Filter> | ||||||
|  |     </ClInclude> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| </Project> | </Project> | ||||||
							
								
								
									
										31
									
								
								include/recomp_port.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								include/recomp_port.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | #ifndef __RECOMP_PORT__ | ||||||
|  | #define __RECOMP_PORT__ | ||||||
|  | 
 | ||||||
|  | #include <span> | ||||||
|  | #include <string_view> | ||||||
|  | #include <cstdint> | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | inline uint32_t byteswap(uint32_t val) { | ||||||
|  |     return _byteswap_ulong(val); | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | constexpr uint32_t byteswap(uint32_t val) { | ||||||
|  |     return __builtin_bswap32(val); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace RecompPort { | ||||||
|  | 
 | ||||||
|  |     struct Function { | ||||||
|  |         uint32_t vram; | ||||||
|  |         uint32_t rom; | ||||||
|  |         const std::span<const uint32_t> words; | ||||||
|  |         std::string name; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     bool recompile_function(const Function& func, std::string_view output_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -133,15 +133,23 @@ | ||||||
|   <PropertyGroup Label="UserMacros" /> |   <PropertyGroup Label="UserMacros" /> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||||
|     <LinkIncremental>true</LinkIncremental> |     <LinkIncremental>true</LinkIncremental> | ||||||
|  |     <OutDir>$(SolutionDir)$(Configuration)\rabbitizer_build\</OutDir> | ||||||
|  |     <IntDir>$(Configuration)\rabbitizer_build\</IntDir> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | ||||||
|     <LinkIncremental>false</LinkIncremental> |     <LinkIncremental>false</LinkIncremental> | ||||||
|  |     <OutDir>$(SolutionDir)$(Configuration)\rabbitizer_build\</OutDir> | ||||||
|  |     <IntDir>$(Configuration)\rabbitizer_build\</IntDir> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||||
|     <LinkIncremental>true</LinkIncremental> |     <LinkIncremental>true</LinkIncremental> | ||||||
|  |     <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\rabbitizer_build\</OutDir> | ||||||
|  |     <IntDir>$(Platform)\$(Configuration)\rabbitizer_build\</IntDir> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||||
|     <LinkIncremental>false</LinkIncremental> |     <LinkIncremental>false</LinkIncremental> | ||||||
|  |     <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\rabbitizer_build\</OutDir> | ||||||
|  |     <IntDir>$(Platform)\$(Configuration)\rabbitizer_build\</IntDir> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||||
|     <Link> |     <Link> | ||||||
|  |  | ||||||
|  | @ -72,15 +72,23 @@ | ||||||
|   <PropertyGroup Label="UserMacros" /> |   <PropertyGroup Label="UserMacros" /> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||||
|     <LinkIncremental>true</LinkIncremental> |     <LinkIncremental>true</LinkIncremental> | ||||||
|  |     <OutDir>$(SolutionDir)$(Configuration)\fmtlib_build\</OutDir> | ||||||
|  |     <IntDir>$(Configuration)\fmtlib_build\</IntDir> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | ||||||
|     <LinkIncremental>false</LinkIncremental> |     <LinkIncremental>false</LinkIncremental> | ||||||
|  |     <OutDir>$(SolutionDir)$(Configuration)\fmtlib_build\</OutDir> | ||||||
|  |     <IntDir>$(Configuration)\fmtlib_build\</IntDir> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||||
|     <LinkIncremental>true</LinkIncremental> |     <LinkIncremental>true</LinkIncremental> | ||||||
|  |     <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\fmtlib_build\</OutDir> | ||||||
|  |     <IntDir>$(Platform)\$(Configuration)\fmtlib_build\</IntDir> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> |   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||||
|     <LinkIncremental>false</LinkIncremental> |     <LinkIncremental>false</LinkIncremental> | ||||||
|  |     <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\fmtlib_build\</OutDir> | ||||||
|  |     <IntDir>$(Platform)\$(Configuration)\fmtlib_build\</IntDir> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||||
|     <ClCompile> |     <ClCompile> | ||||||
|  |  | ||||||
							
								
								
									
										94
									
								
								recomp.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								recomp.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | ||||||
|  | #ifndef __RECOMP_H__ | ||||||
|  | #define __RECOMP_H__ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #define ADD32(a, b) \ | ||||||
|  |     ((uint64_t)(int32_t)((a) + (b))) | ||||||
|  | 
 | ||||||
|  | #define SUB32(a, b) \ | ||||||
|  |     ((uint64_t)(int32_t)((a) - (b))) | ||||||
|  | 
 | ||||||
|  | #define MEM_D(offset, reg) \ | ||||||
|  |     (*(int64_t*)((rdram) + (((reg) + (offset)) ^ 3))) | ||||||
|  | 
 | ||||||
|  | #define MEM_W(offset, reg) \ | ||||||
|  |     (*(int32_t*)((rdram) + (((reg) + (offset)) ^ 3))) | ||||||
|  | 
 | ||||||
|  | #define MEM_H(offset, reg) \ | ||||||
|  |     (*(int16_t*)((rdram) + (((reg) + (offset)) ^ 3))) | ||||||
|  | 
 | ||||||
|  | #define MEM_B(offset, reg) \ | ||||||
|  |     (*(int8_t*)((rdram) + (((reg) + (offset)) ^ 3))) | ||||||
|  | 
 | ||||||
|  | #define MEM_HU(offset, reg) \ | ||||||
|  |     (*(uint16_t*)((rdram) + (((reg) + (offset)) ^ 3))) | ||||||
|  | 
 | ||||||
|  | #define MEM_BU(offset, reg) \ | ||||||
|  |     (*(uint8_t*)((rdram) + (((reg) + (offset)) ^ 3))) | ||||||
|  | 
 | ||||||
|  | #define S32(val) \ | ||||||
|  |     ((int32_t)(val)) | ||||||
|  |      | ||||||
|  | #define U32(val) \ | ||||||
|  |     ((uint32_t)(val)) | ||||||
|  | 
 | ||||||
|  | #define S64(val) \ | ||||||
|  |     ((int64_t)(val)) | ||||||
|  | 
 | ||||||
|  | #define MUL_S(val1, val2) \ | ||||||
|  |     ((val1) * (val2)) | ||||||
|  | 
 | ||||||
|  | #define MUL_D(val1, val2) \ | ||||||
|  |     ((val1) * (val2)) | ||||||
|  | 
 | ||||||
|  | #define DIV_S(val1, val2) \ | ||||||
|  |     ((val1) / (val2)) | ||||||
|  | 
 | ||||||
|  | #define DIV_D(val1, val2) \ | ||||||
|  |     ((val1) / (val2)) | ||||||
|  | 
 | ||||||
|  | #define CVT_S_W(val) \ | ||||||
|  |     ((float)((int32_t)(val))) | ||||||
|  | 
 | ||||||
|  | #define CVT_D_W(val) \ | ||||||
|  |     ((double)((int32_t)(val))) | ||||||
|  | 
 | ||||||
|  | #define CVT_D_S(val) \ | ||||||
|  |     ((double)(val)) | ||||||
|  | 
 | ||||||
|  | #define CVT_S_D(val) \ | ||||||
|  |     ((float)(val)) | ||||||
|  | 
 | ||||||
|  | #define TRUNC_W_S(val) \ | ||||||
|  |     ((int32_t)(val)) | ||||||
|  | 
 | ||||||
|  | #define TRUNC_W_D(val) \ | ||||||
|  |     ((int32_t)(val)) | ||||||
|  | 
 | ||||||
|  | typedef uint64_t gpr; | ||||||
|  | 
 | ||||||
|  | typedef union { | ||||||
|  |     double d; | ||||||
|  |     struct { | ||||||
|  |         float fl; | ||||||
|  |         float fh; | ||||||
|  |     }; | ||||||
|  |     struct { | ||||||
|  |         uint32_t u32l; | ||||||
|  |         uint32_t u32h; | ||||||
|  |     }; | ||||||
|  |     uint64_t u64; | ||||||
|  | } fpr; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     gpr r0,  r1,  r2,  r3,  r4,  r5,  r6,  r7, | ||||||
|  |         r8,  r9,  r10, r11, r12, r13, r14, r15, | ||||||
|  |         r16, r17, r18, r19, r20, r21, r22, r23, | ||||||
|  |         r24, r25, r26, r27, r28, r29, r30, r31; | ||||||
|  |     fpr f0,  f2,  f4,  f6,  f8,  f10, f12, f14, | ||||||
|  |         f16, f18, f20, f22, f24, f26, f28, f30; | ||||||
|  |     uint64_t hi, lo; | ||||||
|  | } recomp_context; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										327
									
								
								src/main.cpp
									
										
									
									
									
								
							
							
						
						
									
										327
									
								
								src/main.cpp
									
										
									
									
									
								
							|  | @ -1,17 +1,330 @@ | ||||||
|  | #include <cstdio> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <unordered_set> | ||||||
|  | #include <span> | ||||||
|  | 
 | ||||||
| #include "rabbitizer.hpp" | #include "rabbitizer.hpp" | ||||||
| #include "elfio/elfio.hpp" | #include "elfio/elfio.hpp" | ||||||
| #include "fmt/format.h" | #include "fmt/format.h" | ||||||
| 
 | 
 | ||||||
| #include <cstdio> | #include "recomp_port.h" | ||||||
|  | 
 | ||||||
|  | std::unordered_set<std::string> ignored_funcs { | ||||||
|  |     // OS initialize functions
 | ||||||
|  |     "__createSpeedParam", | ||||||
|  |     "__osInitialize_common", | ||||||
|  |     "__osInitialize_autodetect", | ||||||
|  |     "osInitialize", | ||||||
|  |     // Audio interface functions
 | ||||||
|  |     "osAiGetLength", | ||||||
|  |     "osAiGetStatus", | ||||||
|  |     "osAiSetFrequency", | ||||||
|  |     "osAiSetNextBuffer", | ||||||
|  |     "__osAiDeviceBusy", | ||||||
|  |     // Video interface functions
 | ||||||
|  |     "osViBlack", | ||||||
|  |     "osViFade", | ||||||
|  |     "osViGetCurrentField", | ||||||
|  |     "osViGetCurrentFramebuffer", | ||||||
|  |     "osViGetCurrentLine", | ||||||
|  |     "osViGetCurrentMode", | ||||||
|  |     "osViGetNextFramebuffer", | ||||||
|  |     "osViGetStatus", | ||||||
|  |     "osViRepeatLine", | ||||||
|  |     "osViSetEvent", | ||||||
|  |     "osViSetMode", | ||||||
|  |     "osViSetSpecialFeatures", | ||||||
|  |     "osViSetXScale", | ||||||
|  |     "osViSetYScale", | ||||||
|  |     "osViSwapBuffer", | ||||||
|  |     "osCreateViManager", | ||||||
|  |     "viMgrMain", | ||||||
|  |     "__osViInit", | ||||||
|  |     "__osViSwapContext", | ||||||
|  |     "__osViGetCurrentContext", | ||||||
|  |     // RDP functions
 | ||||||
|  |     "osDpGetCounters", | ||||||
|  |     "osDpSetStatus", | ||||||
|  |     "osDpGetStatus", | ||||||
|  |     "osDpSetNextBuffer", | ||||||
|  |     "__osDpDeviceBusy", | ||||||
|  |     // RSP functions
 | ||||||
|  |     "osSpTaskLoad", | ||||||
|  |     "osSpTaskStartGo", | ||||||
|  |     "osSpTaskYield", | ||||||
|  |     "osSpTaskYielded", | ||||||
|  |     "__osSpDeviceBusy", | ||||||
|  |     "__osSpGetStatus", | ||||||
|  |     "__osSpRawStartDma", | ||||||
|  |     "__osSpRawReadIo", | ||||||
|  |     "__osSpRawWriteIo", | ||||||
|  |     "__osSpSetPc", | ||||||
|  |     "__osSpSetStatus", | ||||||
|  |     // Controller functions
 | ||||||
|  |     "osContGetQuery", | ||||||
|  |     "osContGetReadData", | ||||||
|  |     "osContInit", | ||||||
|  |     "osContReset", | ||||||
|  |     "osContSetCh", | ||||||
|  |     "osContStartQuery", | ||||||
|  |     "osContStartReadData", | ||||||
|  |     "__osContAddressCrc", | ||||||
|  |     "__osContDataCrc", | ||||||
|  |     "__osContGetInitData", | ||||||
|  |     "__osContRamRead", | ||||||
|  |     "__osContRamWrite", | ||||||
|  |     // EEPROM functions
 | ||||||
|  |     "osEepromLongRead", | ||||||
|  |     "osEepromLongWrite", | ||||||
|  |     "osEepromProbe", | ||||||
|  |     "osEepromRead", | ||||||
|  |     "osEepromWrite", | ||||||
|  |     "__osEepStatus", | ||||||
|  |     // Rumble functions
 | ||||||
|  |     "osMotorInit", | ||||||
|  |     "osMotorStart", | ||||||
|  |     "osMotorStop", | ||||||
|  |     // PFS functions
 | ||||||
|  |     "osPfsAllocateFile", | ||||||
|  |     "osPfsChecker", | ||||||
|  |     "osPfsDeleteFile", | ||||||
|  |     "osPfsFileState", | ||||||
|  |     "osPfsFindFile", | ||||||
|  |     "osPfsFreeBlocks", | ||||||
|  |     "osPfsGetLabel", | ||||||
|  |     "osPfsInit", | ||||||
|  |     "osPfsInitPak", | ||||||
|  |     "osPfsIsPlug", | ||||||
|  |     "osPfsNumFiles", | ||||||
|  |     "osPfsRepairId", | ||||||
|  |     "osPfsReadWriteFile", | ||||||
|  |     "__osPackEepReadData", | ||||||
|  |     "__osPackEepWriteData", | ||||||
|  |     "__osPackRamReadData", | ||||||
|  |     "__osPackRamWriteData", | ||||||
|  |     "__osPackReadData", | ||||||
|  |     "__osPackRequestData", | ||||||
|  |     "__osPfsGetInitData", | ||||||
|  |     "__osPfsGetOneChannelData", | ||||||
|  |     "__osPfsGetStatus", | ||||||
|  |     "__osPfsRequestData", | ||||||
|  |     "__osPfsRequestOneChannel", | ||||||
|  |     "__osPfsCreateAccessQueue", | ||||||
|  |     // Low level serial interface functions
 | ||||||
|  |     "__osSiDeviceBusy", | ||||||
|  |     "__osSiGetStatus", | ||||||
|  |     "__osSiRawStartDma", | ||||||
|  |     "__osSiRawReadIo", | ||||||
|  |     "__osSiRawWriteIo", | ||||||
|  |     "__osSiCreateAccessQueue", | ||||||
|  |     "__osSiGetAccess", | ||||||
|  |     "__osSiRelAccess", | ||||||
|  |     // Parallel interface (cartridge, DMA, etc.) functions
 | ||||||
|  |     "osCartRomInit", | ||||||
|  |     "osLeoDiskInit", | ||||||
|  |     "osCreatePiManager", | ||||||
|  |     "__osDevMgrMain", | ||||||
|  |     "osPiGetCmdQueue", | ||||||
|  |     "osPiGetStatus", | ||||||
|  |     "osPiReadIo", | ||||||
|  |     "osPiStartDma", | ||||||
|  |     "osPiWriteIo", | ||||||
|  |     "osEPiGetDeviceType", | ||||||
|  |     "osEPiStartDma", | ||||||
|  |     "osEPiWriteIo", | ||||||
|  |     "osEPiReadIo", | ||||||
|  |     "osPiRawStartDma", | ||||||
|  |     "osPiRawReadIo", | ||||||
|  |     "osPiRawWriteIo", | ||||||
|  |     "osEPiRawStartDma", | ||||||
|  |     "osEPiRawReadIo", | ||||||
|  |     "osEPiRawWriteIo", | ||||||
|  |     "__osPiRawStartDma", | ||||||
|  |     "__osPiRawReadIo", | ||||||
|  |     "__osPiRawWriteIo", | ||||||
|  |     "__osEPiRawStartDma", | ||||||
|  |     "__osEPiRawReadIo", | ||||||
|  |     "__osEPiRawWriteIo", | ||||||
|  |     "__osPiDeviceBusy", | ||||||
|  |     "__osPiCreateAccessQueue", | ||||||
|  |     "__osPiGetAccess", | ||||||
|  |     "__osPiRelAccess", | ||||||
|  |     "__osLeoAbnormalResume", | ||||||
|  |     "__osLeoInterrupt", | ||||||
|  |     "__osLeoResume", | ||||||
|  |     // Threading functions
 | ||||||
|  |     "osCreateThread", | ||||||
|  |     "osStartThread", | ||||||
|  |     "osStopThread", | ||||||
|  |     "osDestroyThread", | ||||||
|  |     "osYieldThread", | ||||||
|  |     "osSetThreadPri", | ||||||
|  |     "osGetThreadPri", | ||||||
|  |     "osGetThreadId", | ||||||
|  |     "__osDequeueThread", | ||||||
|  |     // Message Queue functions
 | ||||||
|  |     "osCreateMesgQueue", | ||||||
|  |     "osSendMesg", | ||||||
|  |     "osJamMesg", | ||||||
|  |     "osRecvMesg", | ||||||
|  |     "osSetEventMesg", | ||||||
|  |     // Timer functions
 | ||||||
|  |     "osStartTimer", | ||||||
|  |     "osSetTimer", | ||||||
|  |     "osStopTimer", | ||||||
|  |     "__osInsertTimer", | ||||||
|  |     "__osTimerInterrupt", | ||||||
|  |     "__osTimerServicesInit", | ||||||
|  |     "__osSetTimerIntr", | ||||||
|  |     // exceptasm functions
 | ||||||
|  |     "__osExceptionPreamble", | ||||||
|  |     "__osException", | ||||||
|  |     "send_mesg", | ||||||
|  |     "handle_CpU", | ||||||
|  |     "__osEnqueueAndYield", | ||||||
|  |     "__osEnqueueThread", | ||||||
|  |     "__osPopThread", | ||||||
|  |     "__osNop", | ||||||
|  |     "__osDispatchThread", | ||||||
|  |     "__osCleanupThread", | ||||||
|  |     "osGetCurrFaultedThread", | ||||||
|  |     "osGetNextFaultedThread", | ||||||
|  |     // interrupt functions
 | ||||||
|  |     "osSetIntMask", | ||||||
|  |     "osGetIntMask", | ||||||
|  |     "__osDisableInt", | ||||||
|  |     "__osRestoreInt", | ||||||
|  |     "__osSetGlobalIntMask", | ||||||
|  |     "__osResetGlobalIntMask", | ||||||
|  |     // TLB functions
 | ||||||
|  |     "osMapTLB", | ||||||
|  |     "osUnmapTLB", | ||||||
|  |     "osUnmapTLBAll", | ||||||
|  |     "osSetTLBASID", | ||||||
|  |     "osMapTLBRdb", | ||||||
|  |     "osVirtualToPhysical", | ||||||
|  |     "__osGetTLBHi", | ||||||
|  |     "__osGetTLBLo0", | ||||||
|  |     "__osGetTLBLo1", | ||||||
|  |     "__osGetTLBPageMask", | ||||||
|  |     "__osGetTLBASID", | ||||||
|  |     "__osProbeTLB", | ||||||
|  |     // Coprocessor 0 functions
 | ||||||
|  |     "__osSetCount", | ||||||
|  |     "osGetCount", | ||||||
|  |     "__osSetSR", | ||||||
|  |     "__osGetSR", | ||||||
|  |     "__osSetCause", | ||||||
|  |     "__osGetCause", | ||||||
|  |     "__osSetCompare", | ||||||
|  |     "__osGetCompare", | ||||||
|  |     "__osSetConfig", | ||||||
|  |     "__osGetConfig", | ||||||
|  |     "__osSetWatchLo", | ||||||
|  |     "__osGetWatchLo", | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| int main(int argc, char** argv) { | int main(int argc, char** argv) { | ||||||
|     uint32_t word = 0x8D4A7E18; // lw
 |     if (argc != 2) { | ||||||
|     uint32_t vram = 0x80000000; |         fmt::print("Usage: {} [input elf file]\n", argv[0]); | ||||||
|     int extraLJust = 5; |         std::exit(EXIT_SUCCESS); | ||||||
|     rabbitizer::InstructionCpu instr(word, vram); |     } | ||||||
| 
 | 
 | ||||||
|     fmt::print("{}\n", instr.isBranch()); |     ELFIO::elfio elf_file; | ||||||
|     fmt::print("{:08X}: {}\n", word, instr.disassemble(extraLJust)); | 
 | ||||||
|  |     auto exit_failure = [] (const std::string& error_str) { | ||||||
|  |         fmt::print(stderr, error_str); | ||||||
|  |         std::exit(EXIT_FAILURE); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (!elf_file.load(argv[1])) { | ||||||
|  |         exit_failure("Failed to load provided elf file\n"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (elf_file.get_class() != ELFIO::ELFCLASS32) { | ||||||
|  |         exit_failure("Incorrect elf class\n"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (elf_file.get_encoding() != ELFIO::ELFDATA2MSB) { | ||||||
|  |         exit_failure("Incorrect endianness\n"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Pointer to the symbol table section
 | ||||||
|  |     ELFIO::section* symtab_section = nullptr; | ||||||
|  |     // Size of the ROM as determined by the elf
 | ||||||
|  |     ELFIO::Elf_Xword rom_size = 0; | ||||||
|  |     // ROM address of each section
 | ||||||
|  |     std::vector<ELFIO::Elf_Xword> section_rom_addrs{}; | ||||||
|  |     section_rom_addrs.resize(elf_file.sections.size()); | ||||||
|  | 
 | ||||||
|  |     // Iterate over every section to record rom addresses and find the symbol table
 | ||||||
|  |     fmt::print("Sections\n"); | ||||||
|  |     for (const std::unique_ptr<ELFIO::section>& section : elf_file.sections) { | ||||||
|  |         fmt::print("  {}: {} @ 0x{:08X}, 0x{:08X}\n", section->get_index(), section->get_name(), section->get_address(), rom_size); | ||||||
|  |         // Set the rom address of this section to the current accumulated ROM size
 | ||||||
|  |         section_rom_addrs[section->get_index()] = rom_size; | ||||||
|  |         // If this section isn't bss (SHT_NOBITS) and ends up in the rom (SHF_ALLOC), increase the rom size by this section's size
 | ||||||
|  |         if (section->get_type() != ELFIO::SHT_NOBITS && section->get_flags() & ELFIO::SHF_ALLOC) { | ||||||
|  |             rom_size += section->get_size(); | ||||||
|  |         } | ||||||
|  |         // Check if this section is the symbol table and record it if so
 | ||||||
|  |         if (section->get_type() == ELFIO::SHT_SYMTAB) { | ||||||
|  |             symtab_section = section.get(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // If no symbol table was found then exit
 | ||||||
|  |     if (symtab_section == nullptr) { | ||||||
|  |         exit_failure("No symbol section found\n"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ELFIO::symbol_section_accessor symbols{ elf_file, symtab_section }; | ||||||
|  | 
 | ||||||
|  |     fmt::print("Num symbols: {}\n", symbols.get_symbols_num()); | ||||||
|  | 
 | ||||||
|  |     std::vector<RecompPort::Function> functions{}; | ||||||
|  |     functions.reserve(1024); | ||||||
|  | 
 | ||||||
|  |     for (int sym_index = 0; sym_index < symbols.get_symbols_num(); sym_index++) { | ||||||
|  |         std::string   name; | ||||||
|  |         ELFIO::Elf64_Addr    value; | ||||||
|  |         ELFIO::Elf_Xword     size; | ||||||
|  |         unsigned char bind; | ||||||
|  |         unsigned char type; | ||||||
|  |         ELFIO::Elf_Half      section_index; | ||||||
|  |         unsigned char other; | ||||||
|  | 
 | ||||||
|  |         // Read symbol properties
 | ||||||
|  |         symbols.get_symbol(sym_index, name, value, size, bind, type, | ||||||
|  |             section_index, other); | ||||||
|  | 
 | ||||||
|  |         // Check if this symbol is a function
 | ||||||
|  |         if (type == ELFIO::STT_FUNC) { | ||||||
|  |             auto section_rom_addr = section_rom_addrs[section_index]; | ||||||
|  |             auto section_offset = value - elf_file.sections[section_index]->get_address(); | ||||||
|  |             const uint32_t* words = reinterpret_cast<const uint32_t*>(elf_file.sections[section_index]->get_data() + section_offset); | ||||||
|  |             functions.emplace_back( | ||||||
|  |                 static_cast<uint32_t>(value), | ||||||
|  |                 static_cast<uint32_t>(section_offset + section_rom_addr), | ||||||
|  |                 std::span{ reinterpret_cast<const uint32_t*>(words), size / 4 }, | ||||||
|  |                 std::move(name) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fmt::print("Function count: {}\n", functions.size()); | ||||||
|  | 
 | ||||||
|  |     //#pragma omp parallel for
 | ||||||
|  |     for (size_t i = 0; i < functions.size(); i++) { | ||||||
|  |         const auto& func = functions[i]; | ||||||
|  |         if (!ignored_funcs.contains(func.name)) { | ||||||
|  |             if (RecompPort::recompile_function(func, "out/" + func.name + ".c") == false) { | ||||||
|  |                 fmt::print(stderr, "Error recompiling {}\n", func.name); | ||||||
|  |                 std::exit(EXIT_FAILURE); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     //RecompPort::recompile_function(functions.back(), "test.c");
 | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										719
									
								
								src/recompilation.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										719
									
								
								src/recompilation.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,719 @@ | ||||||
|  | #include <vector> | ||||||
|  | #include <set> | ||||||
|  | 
 | ||||||
|  | #include "rabbitizer.hpp" | ||||||
|  | #include "fmt/format.h" | ||||||
|  | #include "fmt/ostream.h" | ||||||
|  | 
 | ||||||
|  | #include "recomp_port.h" | ||||||
|  | 
 | ||||||
|  | using InstrId = rabbitizer::InstrId::UniqueId; | ||||||
|  | 
 | ||||||
|  | std::string_view ctx_gpr_prefix(int reg) { | ||||||
|  |     if (reg != 0) { | ||||||
|  |         return "ctx->r"; | ||||||
|  |     } | ||||||
|  |     return ""; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool process_instruction(size_t instr_index, const std::vector<rabbitizer::InstructionCpu>& instructions, std::ofstream& output_file, bool indent, bool emit_link_branch, int link_branch_index, bool& needs_link_branch, bool& is_branch_likely) { | ||||||
|  |     const auto& instr = instructions[instr_index]; | ||||||
|  |     needs_link_branch = false; | ||||||
|  |     is_branch_likely = false; | ||||||
|  | 
 | ||||||
|  |     // Output a comment with the original instruction
 | ||||||
|  |     if (instr.isBranch() || instr.getUniqueId() == InstrId::cpu_j) { | ||||||
|  |         fmt::print(output_file, "    // {}\n", instr.disassemble(0, fmt::format("L_{:08X}", (uint32_t)instr.getBranchVramGeneric()))); | ||||||
|  |     } else if (instr.getUniqueId() == InstrId::cpu_jal) { | ||||||
|  |         fmt::print(output_file, "    // {}\n", instr.disassemble(0, "func")); | ||||||
|  |     } else { | ||||||
|  |         fmt::print(output_file, "    // {}\n", instr.disassemble(0)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto print_indent = [&]() { | ||||||
|  |         fmt::print(output_file, "    "); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     auto print_line = [&]<typename... Ts>(fmt::format_string<Ts...> fmt_str, Ts ...args) { | ||||||
|  |         print_indent(); | ||||||
|  |         fmt::print(output_file, fmt_str, args...); | ||||||
|  |         fmt::print(output_file, ";\n"); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     auto print_branch_condition = [&]<typename... Ts>(fmt::format_string<Ts...> fmt_str, Ts ...args) { | ||||||
|  |         fmt::print(output_file, fmt_str, args...); | ||||||
|  |         fmt::print(output_file, " "); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     auto print_branch = [&]<typename... Ts>(fmt::format_string<Ts...> fmt_str, Ts ...args) { | ||||||
|  |         fmt::print(output_file, "{{\n    "); | ||||||
|  |         if (instr_index < instructions.size() - 1) { | ||||||
|  |             bool dummy_needs_link_branch; | ||||||
|  |             bool dummy_is_branch_likely; | ||||||
|  |             process_instruction(instr_index + 1, instructions, output_file, true, false, link_branch_index, dummy_needs_link_branch, dummy_is_branch_likely); | ||||||
|  |         } | ||||||
|  |         fmt::print(output_file, "        "); | ||||||
|  |         fmt::print(output_file, fmt_str, args...); | ||||||
|  |         if (needs_link_branch) { | ||||||
|  |             fmt::print(output_file, ";\n        goto after_{}", link_branch_index); | ||||||
|  |         } | ||||||
|  |         fmt::print(output_file, ";\n    }}\n"); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (indent) { | ||||||
|  |         print_indent(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int rd = (int)instr.GetO32_rd(); | ||||||
|  |     int rs = (int)instr.GetO32_rs(); | ||||||
|  |     int base = rs; | ||||||
|  |     int rt = (int)instr.GetO32_rt(); | ||||||
|  |     int sa = (int)instr.Get_sa(); | ||||||
|  | 
 | ||||||
|  |     int fd = (int)instr.GetO32_fd(); | ||||||
|  |     int fs = (int)instr.GetO32_fs(); | ||||||
|  |     int ft = (int)instr.GetO32_ft(); | ||||||
|  | 
 | ||||||
|  |     uint16_t imm = instr.Get_immediate(); | ||||||
|  | 
 | ||||||
|  |     switch (instr.getUniqueId()) { | ||||||
|  |     case InstrId::cpu_nop: | ||||||
|  |         fmt::print(output_file, "\n"); | ||||||
|  |         break; | ||||||
|  |     // Arithmetic
 | ||||||
|  |     case InstrId::cpu_lui: | ||||||
|  |         print_line("{}{} = {:#X} << 16", ctx_gpr_prefix(rt), rt, imm); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_addu: | ||||||
|  |         print_line("{}{} = ADD32({}{}, {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_negu: // pseudo instruction for subu x, 0, y
 | ||||||
|  |     case InstrId::cpu_subu: | ||||||
|  |         print_line("{}{} = SUB32({}{}, {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_addiu: | ||||||
|  |         print_line("{}{} = ADD32({}{}, {:#X})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, (int16_t)imm); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_and: | ||||||
|  |         print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_andi: | ||||||
|  |         print_line("{}{} = {}{} & {:#X}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, imm); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_or: | ||||||
|  |         print_line("{}{} = {}{} | {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_ori: | ||||||
|  |         print_line("{}{} = {}{} | {:#X}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, imm); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_nor: | ||||||
|  |         print_line("{}{} = ~({}{} | {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_xor: | ||||||
|  |         print_line("{}{} = {}{} ^ {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_xori: | ||||||
|  |         print_line("{}{} = {}{} ^ {:#X}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, imm); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sll: | ||||||
|  |         print_line("{}{} = S32({}{}) << {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sllv: | ||||||
|  |         print_line("{}{} = S32({}{}) << ({}{} & 31)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sra: | ||||||
|  |         print_line("{}{} = S32(S64({}{}) >> {})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_srav: | ||||||
|  |         print_line("{}{} = S32(S64({}{}) >> ({}{} & 31)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_srl: | ||||||
|  |         print_line("{}{} = S32(U32({}{}) >> {})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_srlv: | ||||||
|  |         print_line("{}{} = S32(U32({}{}) >> ({}{} & 31)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_slt: | ||||||
|  |         print_line("{}{} = S64({}{}) < S64({}{}) ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_slti: | ||||||
|  |         print_line("{}{} = S64({}{}) < {:#X} ? 1 : 0", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, (int16_t)imm); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sltu: | ||||||
|  |         print_line("{}{} = U64({}{}) < U64({}{}) ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sltiu: | ||||||
|  |         print_line("{}{} = U64({}{}) < {:#X} ? 1 : 0", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, (int16_t)imm); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_mult: | ||||||
|  |         print_line("uint64_t result = S64({}{}) * S64({}{}); lo = S32(result >> 0); hi = S32(result >> 32)", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_multu: | ||||||
|  |         print_line("uint64_t result = {}{} * {}{}; lo = S32(result >> 0); hi = S32(result >> 32)", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_div: | ||||||
|  |         print_line("lo = S32(S64({}{}) / S64({}{})); hi = S32(S64({}{}) % S64({}{}))", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_divu: | ||||||
|  |         print_line("lo = S32(U32({}{}) / U32({}{})); hi = S32(U32({}{}) % U32({}{}))", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_mflo: | ||||||
|  |         print_line("{}{} = lo", ctx_gpr_prefix(rd), rd); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_mfhi: | ||||||
|  |         print_line("{}{} = hi", ctx_gpr_prefix(rd), rd); | ||||||
|  |         break; | ||||||
|  |     // Loads
 | ||||||
|  |     // TODO ld
 | ||||||
|  |     case InstrId::cpu_lw: | ||||||
|  |         print_line("{}{} = MEM_W({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_lh: | ||||||
|  |         print_line("{}{} = MEM_H({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_lb: | ||||||
|  |         print_line("{}{} = MEM_B({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_lhu: | ||||||
|  |         print_line("{}{} = MEM_HU({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_lbu: | ||||||
|  |         print_line("{}{} = MEM_BU({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         break; | ||||||
|  |     // Stores
 | ||||||
|  |     case InstrId::cpu_sw: | ||||||
|  |         print_line("MEM_W({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sh: | ||||||
|  |         print_line("MEM_H({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sb: | ||||||
|  |         print_line("MEM_B({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     // TODO lwl, lwr
 | ||||||
|  |     // examples:
 | ||||||
|  |     // reg =        11111111 01234567
 | ||||||
|  |     // mem @ x =             89ABCDEF
 | ||||||
|  | 
 | ||||||
|  |     // LWL x + 0 -> FFFFFFFF 89ABCDEF
 | ||||||
|  |     // LWL x + 1 -> FFFFFFFF ABCDEF67
 | ||||||
|  |     // LWL x + 2 -> FFFFFFFF CDEF4567
 | ||||||
|  |     // LWL x + 3 -> FFFFFFFF EF234567
 | ||||||
|  | 
 | ||||||
|  |     // LWR x + 0 -> 00000000 01234589
 | ||||||
|  |     // LWR x + 1 -> 00000000 012389AB
 | ||||||
|  |     // LWR x + 2 -> 00000000 0189ABCD
 | ||||||
|  |     // LWR x + 3 -> FFFFFFFF 89ABCDEF
 | ||||||
|  |     case InstrId::cpu_lwl: | ||||||
|  |         print_line("{}{} = MEM_WL({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_lwr: | ||||||
|  |         print_line("{}{} = MEM_WR({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_swl: | ||||||
|  |         print_line("MEM_WL({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_swr: | ||||||
|  |         print_line("MEM_WR({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); | ||||||
|  |         break; | ||||||
|  |          | ||||||
|  |     // Branches
 | ||||||
|  |     case InstrId::cpu_jal: | ||||||
|  |         needs_link_branch = true; | ||||||
|  |         print_indent(); | ||||||
|  |         // TODO lookup function name
 | ||||||
|  |         print_branch("{}(rdram, ctx)", "func"); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_jalr: | ||||||
|  |         needs_link_branch = true; | ||||||
|  |         print_indent(); | ||||||
|  |         // TODO index global function table
 | ||||||
|  |         print_branch("{}(rdram, ctx)", "func_reg"); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_j: | ||||||
|  |     case InstrId::cpu_b: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_jr: | ||||||
|  |         print_indent(); | ||||||
|  |         if (rs == (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) { | ||||||
|  |             print_branch("return"); | ||||||
|  |         } else { | ||||||
|  |             // TODO jump table handling
 | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_bnel: | ||||||
|  |         is_branch_likely = true; | ||||||
|  |         [[fallthrough]]; | ||||||
|  |     case InstrId::cpu_bne: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (S32({}{}) != S32({}{}))", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_beql: | ||||||
|  |         is_branch_likely = true; | ||||||
|  |         [[fallthrough]]; | ||||||
|  |     case InstrId::cpu_beq: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (S32({}{}) == S32({}{}))", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_bnez: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (S32({}{}) != 0)", ctx_gpr_prefix(rs), rs); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_beqz: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (S32({}{}) == 0)", ctx_gpr_prefix(rs), rs); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_bgezl: | ||||||
|  |         is_branch_likely = true; | ||||||
|  |         [[fallthrough]]; | ||||||
|  |     case InstrId::cpu_bgez: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (S32({}{}) >= 0)", ctx_gpr_prefix(rs), rs); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_bgtzl: | ||||||
|  |         is_branch_likely = true; | ||||||
|  |         [[fallthrough]]; | ||||||
|  |     case InstrId::cpu_bgtz: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (S32({}{}) > 0)", ctx_gpr_prefix(rs), rs); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_blezl: | ||||||
|  |         is_branch_likely = true; | ||||||
|  |         [[fallthrough]]; | ||||||
|  |     case InstrId::cpu_blez: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (S32({}{}) <= 0)", ctx_gpr_prefix(rs), rs); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_bltzl: | ||||||
|  |         is_branch_likely = true; | ||||||
|  |         [[fallthrough]]; | ||||||
|  |     case InstrId::cpu_bltz: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (S32({}{}) < 0)", ctx_gpr_prefix(rs), rs); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_break: | ||||||
|  |         print_line("do_break();"); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Cop1 loads/stores
 | ||||||
|  |     case InstrId::cpu_mtc1: | ||||||
|  |         if ((fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.u32l = {}{}", fs, ctx_gpr_prefix(rt), rt); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             // odd fpr
 | ||||||
|  |             print_line("ctx->f{}.u32h = {}{}", fs - 1, ctx_gpr_prefix(rt), rt); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_mfc1: | ||||||
|  |         if ((fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("{}{} = ctx->f{}.u32l", ctx_gpr_prefix(rt), rt, fs); | ||||||
|  |         } else { | ||||||
|  |             // odd fpr
 | ||||||
|  |             print_line("{}{} = ctx->f{}.u32h", ctx_gpr_prefix(rt), rt, fs - 1); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_lwc1: | ||||||
|  |         if ((ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.u32l = MEM_W({:#X}, {}{})", ft, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         } else { | ||||||
|  |             // odd fpr
 | ||||||
|  |             print_line("ctx->f{}.u32h = MEM_W({:#X}, {}{})", ft - 1, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_ldc1: | ||||||
|  |         if ((ft & 1) == 0) { | ||||||
|  |             print_line("ctx->f{}.u64 = MEM_D({:#X}, {}{})", ft, (int16_t)imm, ctx_gpr_prefix(base), base); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand for ldc1: f{}\n", ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_swc1: | ||||||
|  |         if ((ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("MEM_W({:#X}, {}{}) = ctx->f{}.u32l", (int16_t)imm, ctx_gpr_prefix(base), base, ft); | ||||||
|  |         } else { | ||||||
|  |             // odd fpr
 | ||||||
|  |             print_line("MEM_W({:#X}, {}{}) = ctx->f{}.u32h", (int16_t)imm, ctx_gpr_prefix(base), base, ft - 1); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sdc1: | ||||||
|  |         if ((ft & 1) == 0) { | ||||||
|  |             print_line("MEM_D({:#X}, {}{}) = ctx->f{}.u64", (int16_t)imm, ctx_gpr_prefix(base), base, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand for sdc1: f{}\n", ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Cop1 compares
 | ||||||
|  |     case InstrId::cpu_c_lt_s: | ||||||
|  |         if ((fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             print_line("c1cs = ctx->f{}.fl <= ctx->f{}.fl", fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand for c.lt.s: f{} f{}\n", fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_c_lt_d: | ||||||
|  |         if ((fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             print_line("c1cs = ctx->f{}.d <= ctx->f{}.d", fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand for c.lt.d: f{} f{}\n", fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_c_le_s: | ||||||
|  |         if ((fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             print_line("c1cs = ctx->f{}.fl <= ctx->f{}.fl", fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand for c.le.s: f{} f{}\n", fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_c_le_d: | ||||||
|  |         if ((fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             print_line("c1cs = ctx->f{}.d <= ctx->f{}.d", fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand for c.le.d: f{} f{}\n", fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_c_eq_s: | ||||||
|  |         if ((fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             print_line("c1cs = ctx->f{}.fl == ctx->f{}.fl", fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand for c.eq.s: f{} f{}\n", fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_c_eq_d: | ||||||
|  |         if ((fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             print_line("c1cs = ctx->f{}.d == ctx->f{}.d", fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand for c.eq.d: f{} f{}\n", fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |      | ||||||
|  |     // Cop1 branches
 | ||||||
|  |     case InstrId::cpu_bc1tl: | ||||||
|  |         is_branch_likely = true; | ||||||
|  |         [[fallthrough]]; | ||||||
|  |     case InstrId::cpu_bc1t: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (c1cs)", ctx_gpr_prefix(rs), rs); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_bc1fl: | ||||||
|  |         is_branch_likely = true; | ||||||
|  |         [[fallthrough]]; | ||||||
|  |     case InstrId::cpu_bc1f: | ||||||
|  |         print_indent(); | ||||||
|  |         print_branch_condition("if (!c1cs)", ctx_gpr_prefix(rs), rs); | ||||||
|  |         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Cop1 arithmetic
 | ||||||
|  |     case InstrId::cpu_mov_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = ctx->f{}.fl", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for mov.s: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_mov_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = ctx->f{}.d", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for mov.d: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_neg_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = -ctx->f{}.fl", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for neg.s: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_neg_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = -ctx->f{}.d", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for neg.d: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_abs_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = fabsf(ctx->f{}.fl)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for abs.s: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_abs_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = fabs(ctx->f{}.d)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for abs.d: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sqrt_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = sqrtf(ctx->f{}.fl)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for sqrt.s: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sqrt_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = sqrt(ctx->f{}.d)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for sqrt.d: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_add_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = ctx->f{}.fl + ctx->f{}.fl", fd, fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for add.s: f{} f{} f{}\n", fd, fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_add_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = ctx->f{}.d + ctx->f{}.d", fd, fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for add.d: f{} f{} f{}\n", fd, fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sub_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = ctx->f{}.fl - ctx->f{}.fl", fd, fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for sub.s: f{} f{} f{}\n", fd, fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_sub_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = ctx->f{}.d - ctx->f{}.d", fd, fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for sub.d: f{} f{} f{}\n", fd, fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_mul_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = MUL_S(ctx->f{}.fl, ctx->f{}.fl)", fd, fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for mul.s: f{} f{} f{}\n", fd, fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_mul_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = MUL_D(ctx->f{}.d, ctx->f{}.d)", fd, fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for mul.d: f{} f{} f{}\n", fd, fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_div_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = DIV_S(ctx->f{}.fl, ctx->f{}.fl)", fd, fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for div.s: f{} f{} f{}\n", fd, fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_div_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = DIV_D(ctx->f{}.d, ctx->f{}.d)", fd, fs, ft); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for div.d: f{} f{} f{}\n", fd, fs, ft); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_cvt_s_w: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = CVT_S_W(ctx->f{}.u32l)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for cvt.s.w: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_cvt_d_w: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = CVT_D_W(ctx->f{}.u32l)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for cvt.d.w: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_cvt_d_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.d = CVT_D_S(ctx->f{}.fl)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for cvt.d.s: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_cvt_s_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.fl = CVT_S_D(ctx->f{}.d)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for cvt.s.d: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_trunc_w_s: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.u32l = TRUNC_W_S(ctx->f{}.fl)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for trunc.w.s: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case InstrId::cpu_trunc_w_d: | ||||||
|  |         if ((fd & 1) == 0 && (fs & 1) == 0) { | ||||||
|  |             // even fpr
 | ||||||
|  |             print_line("ctx->f{}.u32l = TRUNC_W_D(ctx->f{}.d)", fd, fs); | ||||||
|  |         } else { | ||||||
|  |             fmt::print(stderr, "Invalid operand(s) for trunc.w.d: f{} f{}\n", fd, fs); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         fmt::print(stderr, "Unhandled instruction: {}\n", instr.getOpcodeName()); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (emit_link_branch) { | ||||||
|  |         fmt::print(output_file, "    after_{}:\n", link_branch_index); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool RecompPort::recompile_function(const RecompPort::Function& func, std::string_view output_path) { | ||||||
|  |     fmt::print("Recompiling {}\n", func.name); | ||||||
|  |     std::vector<rabbitizer::InstructionCpu> instructions; | ||||||
|  | 
 | ||||||
|  |     // Open the output file and write the file header
 | ||||||
|  |     std::ofstream output_file{ output_path.data() }; | ||||||
|  |     fmt::print(output_file, | ||||||
|  |         "#include \"recomp.h\"\n" | ||||||
|  |         "\n" | ||||||
|  |         "void {}(uint8_t* restrict rdram, recomp_context* restrict ctx) {{\n" | ||||||
|  |         // these variables shouldn't need to be preserved across function boundaries, so make them local for more efficient output
 | ||||||
|  |         "    uint64_t hi = 0, lo = 0;\n" | ||||||
|  |         "    int c1cs = 0; \n", // cop1 conditional signal
 | ||||||
|  |         func.name); | ||||||
|  | 
 | ||||||
|  |     // Use a set to sort and deduplicate labels
 | ||||||
|  |     std::set<uint32_t> branch_labels; | ||||||
|  |     instructions.reserve(func.words.size()); | ||||||
|  | 
 | ||||||
|  |     // First pass, disassemble each instruction and collect branch labels
 | ||||||
|  |     uint32_t vram = func.vram; | ||||||
|  |     for (uint32_t word : func.words) { | ||||||
|  |         const auto& instr = instructions.emplace_back(byteswap(word), vram); | ||||||
|  | 
 | ||||||
|  |         // If this is a branch or a direct jump, add it to the local label list
 | ||||||
|  |         if (instr.isBranch() || instr.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_j) { | ||||||
|  |             branch_labels.insert((uint32_t)instr.getBranchVramGeneric()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Advance the vram address by the size of one instruction
 | ||||||
|  |         vram += 4; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Second pass, emit code for each instruction and emit labels
 | ||||||
|  |     auto cur_label = branch_labels.cbegin(); | ||||||
|  |     vram = func.vram; | ||||||
|  |     int num_link_branches = 0; | ||||||
|  |     int num_likely_branches = 0; | ||||||
|  |     bool needs_link_branch = false; | ||||||
|  |     bool in_likely_delay_slot = false; | ||||||
|  |     for (size_t instr_index = 0; instr_index < instructions.size(); ++instr_index) { | ||||||
|  |         bool had_link_branch = needs_link_branch; | ||||||
|  |         bool is_branch_likely = false; | ||||||
|  |         // If we're in the delay slot of a likely instruction, emit a goto to skip the instruction before any labels
 | ||||||
|  |         if (in_likely_delay_slot) { | ||||||
|  |             fmt::print(output_file, "    goto skip_{};\n", num_likely_branches); | ||||||
|  |         } | ||||||
|  |         // If there are any other branch labels to insert and we're at the next one, insert it
 | ||||||
|  |         if (cur_label != branch_labels.end() && vram >= *cur_label) { | ||||||
|  |             fmt::print(output_file, "L_{:08X}:\n", *cur_label); | ||||||
|  |             ++cur_label; | ||||||
|  |         } | ||||||
|  |         // Process the current instruction and check for errors
 | ||||||
|  |         if (process_instruction(instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, needs_link_branch, is_branch_likely) == false) { | ||||||
|  |             fmt::print(stderr, "Error in recompilation, clearing {}\n", output_path); | ||||||
|  |             output_file.clear(); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         // If a link return branch was generated, advance the number of link return branches
 | ||||||
|  |         if (had_link_branch) { | ||||||
|  |             num_link_branches++; | ||||||
|  |         } | ||||||
|  |         // Now that the instruction has been processed, emit a skip label for the likely branch if needed
 | ||||||
|  |         if (in_likely_delay_slot) { | ||||||
|  |             fmt::print(output_file, "    skip_{}:\n", num_likely_branches); | ||||||
|  |             num_likely_branches++; | ||||||
|  |         } | ||||||
|  |         // Mark the next instruction as being in a likely delay slot if the 
 | ||||||
|  |         in_likely_delay_slot = is_branch_likely; | ||||||
|  |         // Advance the vram address by the size of one instruction
 | ||||||
|  |         vram += 4; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Terminate the function
 | ||||||
|  |     fmt::print(output_file, "}}\n"); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Mr-Wiseguy
						Mr-Wiseguy