mirror of
https://github.com/Mr-Wiseguy/N64Recomp.git
synced 2024-12-28 02:16:09 +00:00
Implemented initial set of instructions and ignored functions
This commit is contained in:
parent
4b1dc14019
commit
8a0f0da0cc
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…
Reference in a new issue