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 | ||||
| *.elf | ||||
| 
 | ||||
| # Output C files | ||||
| out/ | ||||
| 
 | ||||
| # Linux build output | ||||
| build/ | ||||
| *.o | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ | |||
|     <VCProjectVersion>16.0</VCProjectVersion> | ||||
|     <ProjectGuid>{23C26E84-DC01-43A6-B11B-0B4A2D79A5DD}</ProjectGuid> | ||||
|     <Keyword>Win32Proj</Keyword> | ||||
|     <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> | ||||
|   </PropertyGroup> | ||||
|   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> | ||||
|  | @ -75,8 +76,9 @@ | |||
|       <WarningLevel>Level3</WarningLevel> | ||||
|       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> | ||||
|       <Optimization>Disabled</Optimization> | ||||
|       <LanguageStandard>stdcpp17</LanguageStandard> | ||||
|       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||
|       <LanguageStandard>stdcpp20</LanguageStandard> | ||||
|       <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> | ||||
|     <Link> | ||||
|       <TargetMachine>MachineX86</TargetMachine> | ||||
|  | @ -91,8 +93,9 @@ | |||
|       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> | ||||
|       <WarningLevel>Level3</WarningLevel> | ||||
|       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> | ||||
|       <LanguageStandard>stdcpp17</LanguageStandard> | ||||
|       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||
|       <LanguageStandard>stdcpp20</LanguageStandard> | ||||
|       <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> | ||||
|     <Link> | ||||
|       <TargetMachine>MachineX86</TargetMachine> | ||||
|  | @ -105,8 +108,9 @@ | |||
|   </ItemDefinitionGroup> | ||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||
|     <ClCompile> | ||||
|       <LanguageStandard>stdcpp17</LanguageStandard> | ||||
|       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||
|       <LanguageStandard>stdcpp20</LanguageStandard> | ||||
|       <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> | ||||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|  | @ -115,8 +119,9 @@ | |||
|   </ItemDefinitionGroup> | ||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||
|     <ClCompile> | ||||
|       <LanguageStandard>stdcpp17</LanguageStandard> | ||||
|       <AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||
|       <LanguageStandard>stdcpp20</LanguageStandard> | ||||
|       <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> | ||||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|  | @ -133,8 +138,10 @@ | |||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="src\main.cpp" /> | ||||
|     <ClCompile Include="src\recompilation.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="include\recomp_port.h" /> | ||||
|     <ClInclude Include="lib\ELFIO\elfio\elfio.hpp" /> | ||||
|   </ItemGroup> | ||||
|   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||||
|  |  | |||
|  | @ -18,10 +18,16 @@ | |||
|     <ClCompile Include="src\main.cpp"> | ||||
|       <Filter>Source Files</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="src\recompilation.cpp"> | ||||
|       <Filter>Source Files</Filter> | ||||
|     </ClCompile> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="lib\ELFIO\elfio\elfio.hpp"> | ||||
|       <Filter>Header Files</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="include\recomp_port.h"> | ||||
|       <Filter>Header Files</Filter> | ||||
|     </ClInclude> | ||||
|   </ItemGroup> | ||||
| </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 Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||
|     <LinkIncremental>true</LinkIncremental> | ||||
|     <OutDir>$(SolutionDir)$(Configuration)\rabbitizer_build\</OutDir> | ||||
|     <IntDir>$(Configuration)\rabbitizer_build\</IntDir> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | ||||
|     <LinkIncremental>false</LinkIncremental> | ||||
|     <OutDir>$(SolutionDir)$(Configuration)\rabbitizer_build\</OutDir> | ||||
|     <IntDir>$(Configuration)\rabbitizer_build\</IntDir> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||
|     <LinkIncremental>true</LinkIncremental> | ||||
|     <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\rabbitizer_build\</OutDir> | ||||
|     <IntDir>$(Platform)\$(Configuration)\rabbitizer_build\</IntDir> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||
|     <LinkIncremental>false</LinkIncremental> | ||||
|     <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\rabbitizer_build\</OutDir> | ||||
|     <IntDir>$(Platform)\$(Configuration)\rabbitizer_build\</IntDir> | ||||
|   </PropertyGroup> | ||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||
|     <Link> | ||||
|  |  | |||
|  | @ -72,15 +72,23 @@ | |||
|   <PropertyGroup Label="UserMacros" /> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||
|     <LinkIncremental>true</LinkIncremental> | ||||
|     <OutDir>$(SolutionDir)$(Configuration)\fmtlib_build\</OutDir> | ||||
|     <IntDir>$(Configuration)\fmtlib_build\</IntDir> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | ||||
|     <LinkIncremental>false</LinkIncremental> | ||||
|     <OutDir>$(SolutionDir)$(Configuration)\fmtlib_build\</OutDir> | ||||
|     <IntDir>$(Configuration)\fmtlib_build\</IntDir> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||
|     <LinkIncremental>true</LinkIncremental> | ||||
|     <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\fmtlib_build\</OutDir> | ||||
|     <IntDir>$(Platform)\$(Configuration)\fmtlib_build\</IntDir> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||
|     <LinkIncremental>false</LinkIncremental> | ||||
|     <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\fmtlib_build\</OutDir> | ||||
|     <IntDir>$(Platform)\$(Configuration)\fmtlib_build\</IntDir> | ||||
|   </PropertyGroup> | ||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||
|     <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 "elfio/elfio.hpp" | ||||
| #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) { | ||||
|     uint32_t word = 0x8D4A7E18; // lw
 | ||||
|     uint32_t vram = 0x80000000; | ||||
|     int extraLJust = 5; | ||||
|     rabbitizer::InstructionCpu instr(word, vram); | ||||
|     if (argc != 2) { | ||||
|         fmt::print("Usage: {} [input elf file]\n", argv[0]); | ||||
|         std::exit(EXIT_SUCCESS); | ||||
|     } | ||||
| 
 | ||||
|     fmt::print("{}\n", instr.isBranch()); | ||||
|     fmt::print("{:08X}: {}\n", word, instr.disassemble(extraLJust)); | ||||
|     ELFIO::elfio elf_file; | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										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