├── .gitignore ├── LICENSE ├── README.md ├── mipsdump.sln ├── mipsdump.test ├── Program.fs ├── Tests.fs └── mipsdump.test.fsproj └── mipsdump ├── Disassembler.fs ├── Idc.fs ├── Instructions.fs ├── Program.fs └── mipsdump.fsproj /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Luciano Ciccariello 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mipsdump 2 | 3 | A functional MIPS disassembler written in F# that produces 1:1 assembly code that can be compiled with GNU's `as`. 4 | 5 | ## Why? 6 | 7 | I did not like the output from `mips-linux-gnu-objdump` as it needed some corrections to be compiled again by `mips-linux-gnu-as` and it lacked labels support from binary files. 8 | 9 | IDA and Ghidra also produces an output that I am not a fan of, but as it contains labels I am adding support to parse an IDC file to import all the offsets. 10 | 11 | Finally I never worked on a F# project before this one, so this was be a good opportunity to learn with hands on. 12 | 13 | ## Compile from the source code 14 | 15 | The tool can be compiled and used from any operating system (Windows, Linux, macOS) and from any processor architecture (x86, x64, ARM, ARM64). 16 | 17 | 1. Install the latest version of [.NET Core SDK](https://dotnet.microsoft.com/download) 18 | 1. Download the source code and nagivate to the folder `mipsdump` from your terminal 19 | 1. Run `dotnet publish -c Release` 20 | 1. The generated program will now be located at `./bin/Release/net6.0/publish` 21 | 22 | ## Usage 23 | 24 | Once you have the executable, invoke the following command on your terminal to receive help on all the current available features: 25 | 26 | ```shell 27 | ./mipsdump --help 28 | ``` 29 | 30 | ## Features 31 | 32 | This is perfectly compatible with MIPS-I processors, used for example by PlayStation 1 video-games. 33 | 34 | ### Inline compiler 35 | 36 | Allows to generate MIPS-compatible binary code: 37 | 38 | C-style function: 39 | 40 | ```c 41 | int sum(int a, int b) 42 | { 43 | return a + b; 44 | } 45 | ``` 46 | 47 | Functionally identical using the inline compiler: 48 | 49 | ```fsharp 50 | open Instructions 51 | open type Instructions.Reg 52 | 53 | let sum = [| 54 | JR RA; 55 | ADD V0 A0 A1; 56 | |] 57 | ``` 58 | 59 | Some instructions do not accept large numbers. For instance `addi` accepts a 16-bit signed number, so you need to pass a `int16` to let the compiler to be happy: 60 | 61 | ```fsharp 62 | let n = int16 -123 63 | let op1 = ADDI A0 A1 n 64 | let op2 = ADDIU V0 A0 (uint16 123) 65 | ``` 66 | 67 | ### Decompile single instruction 68 | 69 | `Disassembler.disassembleInstr` will decompile a single `uint32` into assembly code. It also accepts [flags](#customise-the-disassembly-with-flags). 70 | 71 | For example, `disassembleInstr 0x00851021u 0x0u Map.Empty Flags.None` will produce `addu $v0, $a0, $a1`. 72 | 73 | ### Generate labels 74 | 75 | By default the program will scan the assembly for branches and jumps to generate labels, useful to have branching in the code more readable. 76 | 77 | Without labels generation: 78 | 79 | ```asm 80 | sw $zero, ($s0) 81 | addiu $s0, $s0, -4 82 | bnez $a0, -4 83 | addiu $a0, $a0, -1 84 | ``` 85 | 86 | With labels generation: 87 | 88 | ```asm 89 | loc_80010914: 90 | sw $zero, ($s0) 91 | addiu $s0, $s0, -4 92 | bnez $a0, loc_80010914 93 | addiu $a0, $a0, -1 94 | ``` 95 | 96 | ### Import labels 97 | 98 | IDA is one of the most famous products to disassemble and decompile softwares. You can create rename generated labels and export the database as IDC and share it across your team or internet. This tool is able to parse the IDC file to capture the user-defined labels. 99 | 100 | ### Customise the disassembly with flags 101 | 102 | * `UseAlias`: instructions such as `or $v0, $a0, $zero` are print as `move $v0, $a0` 103 | * `NoBranchAnalysis`: disable generation of labels 104 | 105 | ## Resources 106 | 107 | The following [MIPS Instruction Set reference by Charles Price](https://www.cs.cmu.edu/afs/cs/academic/class/15740-f97/public/doc/mips-isa.pdf) had everything I needed to know to build this tool. 108 | 109 | ## Many thanks 110 | 111 | Thanks to my friend [fatim](https://github.com/fatim) who introduced me in F# and that helped me to start this journey :) 112 | -------------------------------------------------------------------------------- /mipsdump.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31919.166 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "mipsdump", "mipsdump\mipsdump.fsproj", "{F9898636-B738-4FD6-9FFD-8344ADF1428B}" 7 | EndProject 8 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "mipsdump.test", "mipsdump.test\mipsdump.test.fsproj", "{089A2A59-0CC6-48D5-AF72-11EE03B18F83}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F9898636-B738-4FD6-9FFD-8344ADF1428B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F9898636-B738-4FD6-9FFD-8344ADF1428B}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F9898636-B738-4FD6-9FFD-8344ADF1428B}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F9898636-B738-4FD6-9FFD-8344ADF1428B}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {089A2A59-0CC6-48D5-AF72-11EE03B18F83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {089A2A59-0CC6-48D5-AF72-11EE03B18F83}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {089A2A59-0CC6-48D5-AF72-11EE03B18F83}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {089A2A59-0CC6-48D5-AF72-11EE03B18F83}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {72579EC9-6090-48E8-87F1-1EA28514FA28} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /mipsdump.test/Program.fs: -------------------------------------------------------------------------------- 1 | module Program = let [] main _ = 0 2 | -------------------------------------------------------------------------------- /mipsdump.test/Tests.fs: -------------------------------------------------------------------------------- 1 | // DO NOT MODIFY THE COMMENTED LINES BELOW 2 | // MIT License 3 | // Copyright (c) 2022 Luciano Ciccariello 4 | // https://github.com/Xeeynamo/OpenKh/blob/master/LICENSE 5 | 6 | module Tests 7 | 8 | open Xunit 9 | open Instructions 10 | open type Instructions.Reg 11 | open Disassembler 12 | 13 | [] 14 | let ``Assemble instructions`` () = 15 | // R-type instructions 16 | Assert.Equal(0x001afec0u, SLL RA K0 0x1bu) 17 | Assert.Equal(0x001afec2u, SRL RA K0 0x1bu) 18 | Assert.Equal(0x001afec3u, SRA RA K0 0x1bu) 19 | Assert.Equal(0x037af804u, SLLV RA K0 K1) 20 | Assert.Equal(0x037af806u, SRLV RA K0 K1) 21 | Assert.Equal(0x037af807u, SRAV RA K0 K1) 22 | Assert.Equal(0x03e00008u, JR RA) 23 | Assert.Equal(0x03e00009u, JALR RA) 24 | Assert.Equal(0x03fb72ccu, SYSCALL 0xfedcbu) 25 | Assert.Equal(0x03cc8d0du, BREAK 0x3ccu 0x234u) 26 | Assert.Equal(0x0000f810u, MFHI RA) 27 | Assert.Equal(0x0000f811u, MTHI RA) 28 | Assert.Equal(0x0000f812u, MFLO RA) 29 | Assert.Equal(0x0000f813u, MTLO RA) 30 | Assert.Equal(0x03fa0018u, MULT RA K0) 31 | Assert.Equal(0x03fa0019u, MULTU RA K0) 32 | Assert.Equal(0x03fa001Au, DIV RA K0) 33 | Assert.Equal(0x03fa001Bu, DIVU RA K0) 34 | Assert.Equal(0x035bf820u, ADD RA K0 K1) 35 | Assert.Equal(0x035bf821u, ADDU RA K0 K1) 36 | Assert.Equal(0x035bf822u, SUB RA K0 K1) 37 | Assert.Equal(0x035bf823u, SUBU RA K0 K1) 38 | Assert.Equal(0x035bf824u, AND RA K0 K1) 39 | Assert.Equal(0x035bf825u, OR RA K0 K1) 40 | Assert.Equal(0x035bf826u, XOR RA K0 K1) 41 | Assert.Equal(0x035bf827u, NOR RA K0 K1) 42 | Assert.Equal(0x035bf82au, SLT RA K0 K1) 43 | Assert.Equal(0x035bf82bu, SLTU RA K0 K1) 44 | 45 | // Alias R-type instructions 46 | Assert.Equal(0x00000000u, NOP) 47 | Assert.Equal(0x0340f825u, MOVE RA K0) 48 | Assert.Equal(0x001af822u, NEG RA K0) 49 | Assert.Equal(0x001af823u, NEGU RA K0) 50 | 51 | // I-type instructions 52 | Assert.Equal(0x07503333u, BLTZAL K0 (int16 0x3333)) 53 | Assert.Equal(0x07513333u, BGEZAL K0 (int16 0x3333)) 54 | Assert.Equal(0x07e03333u, BLTZ RA (int16 0x3333)) 55 | Assert.Equal(0x07e13333u, BGEZ RA (int16 0x3333)) 56 | 57 | Assert.Equal(0x13fa3333u, BEQ RA K0 (int16 0x3333)) 58 | Assert.Equal(0x17fa3333u, BNE RA K0 (int16 0x3333)) 59 | Assert.Equal(0x1be03333u, BLEZ RA (int16 0x3333)) 60 | Assert.Equal(0x1fe03333u, BGTZ RA (int16 0x3333)) 61 | Assert.Equal(0x235f3333u, ADDI RA K0 (int16 0x3333)) 62 | Assert.Equal(0x275f3333u, ADDIU RA K0 (uint16 0x3333)) 63 | Assert.Equal(0x2b5f3333u, SLTI RA K0 (int16 0x3333)) 64 | Assert.Equal(0x2f5f3333u, SLTIU RA K0 (int16 0x3333)) 65 | Assert.Equal(0x335f3333u, ANDI RA K0 (uint16 0x3333)) 66 | Assert.Equal(0x375f3333u, ORI RA K0 (uint16 0x3333)) 67 | Assert.Equal(0x3B5f3333u, XORI RA K0 (uint16 0x3333)) 68 | Assert.Equal(0x3c1fccccu, LUI RA (uint16 0xcccc)) 69 | Assert.Equal(0x835f3333u, LB RA K0 (int16 0x3333)) 70 | Assert.Equal(0x875f3333u, LH RA K0 (int16 0x3333)) 71 | Assert.Equal(0x8B5f3333u, LWL RA K0 (int16 0x3333)) 72 | Assert.Equal(0x8F5f3333u, LW RA K0 (int16 0x3333)) 73 | Assert.Equal(0x935f3333u, LBU RA K0 (int16 0x3333)) 74 | Assert.Equal(0x975f3333u, LHU RA K0 (int16 0x3333)) 75 | Assert.Equal(0x9B5f3333u, LWR RA K0 (int16 0x3333)) 76 | 77 | Assert.Equal(0xa35f3333u, SB RA K0 (int16 0x3333)) 78 | Assert.Equal(0xa75f3333u, SH RA K0 (int16 0x3333)) 79 | Assert.Equal(0xab5f3333u, SWL RA K0 (int16 0x3333)) 80 | Assert.Equal(0xaf5f3333u, SW RA K0 (int16 0x3333)) 81 | Assert.Equal(0xbb5f3333u, SWR RA K0 (int16 0x3333)) 82 | 83 | // Alias I-type instructions 84 | Assert.Equal(0x07513333u, BAL K0 (int16 0x3333)) 85 | Assert.Equal(0x241f7fffu, LI RA (int16 0x7fff)) 86 | Assert.Equal(0x341fccccu, LIU RA (uint16 0xcccc)) 87 | 88 | // J-type instructions 89 | Assert.Equal(0x0bccddeeu, J 0x3ccddeeu) 90 | Assert.Equal(0x0fccddeeu, JAL 0x3ccddeeu) 91 | 92 | // Co-processor instructions 93 | Assert.Equal(0x401fd000u, MFC0 RA 26u) 94 | Assert.Equal(0x441fd000u, MFC1 RA 26u) 95 | Assert.Equal(0x481fd000u, MFC2 RA 26u) 96 | Assert.Equal(0x4c1fd000u, MFC3 RA 26u) 97 | Assert.Equal(0x4084a000u, MTC0 A0 20u) 98 | Assert.Equal(0x4484a000u, MTC1 A0 20u) 99 | Assert.Equal(0x4884a000u, MTC2 A0 20u) 100 | Assert.Equal(0x4c84a000u, MTC3 A0 20u) 101 | Assert.Equal(0x4044a000u, CFC0 A0 20u) 102 | Assert.Equal(0x4444a000u, CFC1 A0 20u) 103 | Assert.Equal(0x4844a000u, CFC2 A0 20u) 104 | Assert.Equal(0x4c44a000u, CFC3 A0 20u) 105 | Assert.Equal(0x40c4a000u, CTC0 A0 20u) 106 | Assert.Equal(0x44c4a000u, CTC1 A0 20u) 107 | Assert.Equal(0x48c4a000u, CTC2 A0 20u) 108 | Assert.Equal(0x4cc4a000u, CTC3 A0 20u) 109 | Assert.Equal(0xc3fa3333u, LWC0 26u RA (int16 0x3333)) 110 | Assert.Equal(0xc7fa3333u, LWC1 26u RA (int16 0x3333)) 111 | Assert.Equal(0xcbfa3333u, LWC2 26u RA (int16 0x3333)) 112 | Assert.Equal(0xcffa3333u, LWC3 26u RA (int16 0x3333)) 113 | Assert.Equal(0xe3fa3333u, SWC0 26u RA (int16 0x3333)) 114 | Assert.Equal(0xe7fa3333u, SWC1 26u RA (int16 0x3333)) 115 | Assert.Equal(0xebfa3333u, SWC2 26u RA (int16 0x3333)) 116 | Assert.Equal(0xeffa3333u, SWC3 26u RA (int16 0x3333)) 117 | Assert.Equal(0x41003333u, BC0F (int16 0x3333)) 118 | Assert.Equal(0x41013333u, BC0T (int16 0x3333)) 119 | Assert.Equal(0x45003333u, BC1F (int16 0x3333)) 120 | Assert.Equal(0x45013333u, BC1T (int16 0x3333)) 121 | Assert.Equal(0x49003333u, BC2F (int16 0x3333)) 122 | Assert.Equal(0x49013333u, BC2T (int16 0x3333)) 123 | Assert.Equal(0x4d003333u, BC3F (int16 0x3333)) 124 | Assert.Equal(0x4d013333u, BC3T (int16 0x3333)) 125 | Assert.Equal(0x43234567u, COP0 0x1234567u) 126 | Assert.Equal(0x47234567u, COP1 0x1234567u) 127 | Assert.Equal(0x4a123456u, COP2 0x123456u) 128 | Assert.Equal(0x4e123456u, COP3 0x123456u) 129 | 130 | let labels = [|(0x80010df4u, "TestFunction")|] |> Map.ofArray 131 | let assertDisasm (instr: uint) (expected: string) = 132 | let actual = disassembleInstr instr 0u labels Flags.UseAlias 133 | Assert.Equal(expected, actual) 134 | 135 | [] 136 | let ``Basic instructions`` () = 137 | assertDisasm (SLL V0 A0 6u) "sll\t$v0, $a0, 6" 138 | assertDisasm (SRL V0 A0 12u) "srl\t$v0, $a0, 12" 139 | assertDisasm (SRA V0 A0 18u) "sra\t$v0, $a0, 18" 140 | assertDisasm (SLLV V0 A0 A1) "sllv\t$v0, $a0, $a1" 141 | assertDisasm (SRAV V0 A0 A1) "srav\t$v0, $a0, $a1" 142 | assertDisasm (SRLV V0 A0 A1) "srlv\t$v0, $a0, $a1" 143 | assertDisasm (SYSCALL 0x123u) "syscall\t0x123" 144 | assertDisasm (BREAK 0x123u 0x222u) "break\t0x123, 0x222" 145 | assertDisasm (BREAK 0x123u 0x222u) "break\t0x123, 0x222" 146 | assertDisasm (MFHI V0) "mfhi\t$v0" 147 | assertDisasm (MTHI V0) "mthi\t$v0" 148 | assertDisasm (MFLO V0) "mflo\t$v0" 149 | assertDisasm (MTLO V0) "mtlo\t$v0" 150 | assertDisasm (MULT V0 A0) "mult\t$v0, $a0" 151 | assertDisasm (MULTU V0 A0) "multu\t$v0, $a0" 152 | assertDisasm (DIV V0 A0) "div\t$zero, $v0, $a0" 153 | assertDisasm (DIVU V0 A0) "divu\t$zero, $v0, $a0" 154 | assertDisasm (ADD V0 A0 A1) "add\t$v0, $a0, $a1" 155 | assertDisasm (ADDU V0 A0 A1) "addu\t$v0, $a0, $a1" 156 | assertDisasm (SUB V0 A0 A1) "sub\t$v0, $a0, $a1" 157 | assertDisasm (SUBU V0 A0 A1) "subu\t$v0, $a0, $a1" 158 | assertDisasm (AND V0 A0 A1) "and\t$v0, $a0, $a1" 159 | assertDisasm (OR V0 A0 A1) "or\t$v0, $a0, $a1" 160 | assertDisasm (XOR V0 A0 A1) "xor\t$v0, $a0, $a1" 161 | assertDisasm (NOR V0 A0 A1) "nor\t$v0, $a0, $a1" 162 | assertDisasm (SLT V0 A0 A1) "slt\t$v0, $a0, $a1" 163 | assertDisasm (SLTU V0 A0 A1) "sltu\t$v0, $a0, $a1" 164 | assertDisasm (JR RA) "jr\t$ra" 165 | assertDisasm (JALR RA) "jalr\t$ra" 166 | 167 | assertDisasm (ADDI V0 A0 (int16 -3)) "addi\t$v0, $a0, -3" 168 | assertDisasm (ADDIU V0 A0 (uint16 50000)) "addiu\t$v0, $a0, 50000" 169 | assertDisasm (SLTI V0 A0 (int16 3)) "slti\t$v0, $a0, 3" 170 | assertDisasm (SLTIU V0 A0 (int16 3)) "sltiu\t$v0, $a0, 3" 171 | assertDisasm (ANDI V0 A0 (uint16 3)) "andi\t$v0, $a0, 0x3" 172 | assertDisasm (ORI V0 A0 (uint16 3)) "ori\t$v0, $a0, 0x3" 173 | assertDisasm (XORI V0 A0 (uint16 3)) "xori\t$v0, $a0, 0x3" 174 | assertDisasm(LUI S0 (uint16 100)) "lui\t$s0, 0x64" 175 | 176 | [] 177 | let ``Instructions with pointer`` () = 178 | assertDisasm (LB V0 S0 (int16 100)) "lb\t$v0, 0x64($s0)" 179 | assertDisasm (LH V0 S0 (int16 -100)) "lh\t$v0, -0x64($s0)" 180 | assertDisasm (LWL V0 S0 (int16 100)) "lwl\t$v0, 0x64($s0)" 181 | assertDisasm (LW V0 S0 (int16 100)) "lw\t$v0, 0x64($s0)" 182 | assertDisasm (LBU V0 S0 (int16 100)) "lbu\t$v0, 0x64($s0)" 183 | assertDisasm (LHU V0 S0 (int16 100)) "lhu\t$v0, 0x64($s0)" 184 | assertDisasm (LWR V0 S0 (int16 100)) "lwr\t$v0, 0x64($s0)" 185 | assertDisasm (SB V0 S0 (int16 100)) "sb\t$v0, 0x64($s0)" 186 | assertDisasm (SH V0 S0 (int16 -100)) "sh\t$v0, -0x64($s0)" 187 | assertDisasm (SWL V0 S0 (int16 100)) "swl\t$v0, 0x64($s0)" 188 | assertDisasm (SW V0 S0 (int16 100)) "sw\t$v0, 0x64($s0)" 189 | assertDisasm (SWR V0 S0 (int16 0)) "swr\t$v0, ($s0)" 190 | 191 | [] 192 | let ``Coprocessor instructions`` () = 193 | assertDisasm (MFC0 V0 20u) "mfc0\t$v0, $20" 194 | assertDisasm (MFC1 V0 20u) "mfc1\t$v0, $20" 195 | assertDisasm (MFC2 V0 20u) "mfc2\t$v0, $20" 196 | assertDisasm (MFC3 V0 20u) "mfc3\t$v0, $20" 197 | assertDisasm (MTC0 V0 20u) "mtc0\t$v0, $20" 198 | assertDisasm (MTC1 V0 20u) "mtc1\t$v0, $20" 199 | assertDisasm (MTC2 V0 20u) "mtc2\t$v0, $20" 200 | assertDisasm (MTC3 V0 20u) "mtc3\t$v0, $20" 201 | assertDisasm (CFC0 V0 20u) "cfc0\t$v0, $20" 202 | assertDisasm (CFC1 V0 20u) "cfc1\t$v0, $20" 203 | assertDisasm (CFC2 V0 20u) "cfc2\t$v0, $20" 204 | assertDisasm (CFC3 V0 20u) "cfc3\t$v0, $20" 205 | assertDisasm (CTC0 V0 20u) "ctc0\t$v0, $20" 206 | assertDisasm (CTC1 V0 20u) "ctc1\t$v0, $20" 207 | assertDisasm (CTC2 V0 20u) "ctc2\t$v0, $20" 208 | assertDisasm (CTC3 V0 20u) "ctc3\t$v0, $20" 209 | assertDisasm (LWC0 20u A0 (int16 0x20)) "lwc0\t$20, 0x20($a0)" 210 | assertDisasm (LWC1 20u A0 (int16 0x20)) "lwc1\t$20, 0x20($a0)" 211 | assertDisasm (LWC2 20u A0 (int16 0x20)) "lwc2\t$20, 0x20($a0)" 212 | assertDisasm (LWC3 20u A0 (int16 0x20)) "lwc3\t$20, 0x20($a0)" 213 | assertDisasm (SWC0 20u A0 (int16 0x20)) "swc0\t$20, 0x20($a0)" 214 | assertDisasm (SWC1 20u A0 (int16 0x20)) "swc1\t$20, 0x20($a0)" 215 | assertDisasm (SWC2 20u A0 (int16 0x20)) "swc2\t$20, 0x20($a0)" 216 | assertDisasm (SWC3 20u A0 (int16 -0x20)) "swc3\t$20, -0x20($a0)" 217 | assertDisasm (COP0 0u) "cop0\t0x0" 218 | assertDisasm (COP1 0x80000u) "cop1\t0x80000" 219 | assertDisasm (COP2 0x1FFFFFFu) "cop2\t0x1ffffff" 220 | assertDisasm (COP3 0x2000000u) "cop3\t0x0" 221 | 222 | [] 223 | let ``Jump instructions`` () = 224 | assertDisasm (J 1u) "j\t0x80000004" 225 | assertDisasm (J 0x3FFFFFFu) "j\t0x8ffffffc" 226 | assertDisasm (JAL 0x140u) "jal\t0x80000500" 227 | 228 | assertDisasm (JAL 0x437Du) "jal\tTestFunction" 229 | 230 | [] 231 | let ``Branch instructions`` () = 232 | assertDisasm (BEQ A0 S0 (int16 -1)) "beq\t$a0, $s0, 0x0" 233 | assertDisasm (BNE A0 S0 (int16 -1)) "bne\t$a0, $s0, 0x0" 234 | 235 | assertDisasm (BLEZ A0 (int16 -1)) "blez\t$a0, 0x0" 236 | assertDisasm (BGTZ A0 (int16 -1)) "bgtz\t$a0, 0x0" 237 | 238 | assertDisasm (BLTZ A0 (int16 -1)) "bltz\t$a0, 0x0" 239 | assertDisasm (BGEZ A0 (int16 -1)) "bgez\t$a0, 0x0" 240 | assertDisasm (BLTZAL A0 (int16 -1)) "bltzal\t$a0, 0x0" 241 | assertDisasm (BGEZAL A0 (int16 -1)) "bgezal\t$a0, 0x0" 242 | 243 | assertDisasm (BEQ A0 S0 (int16 -3)) "beq\t$a0, $s0, -0x4" 244 | assertDisasm (BEQ A0 S0 (int16 -5)) "beq\t$a0, $s0, -0x8" 245 | assertDisasm (BEQ A0 S0 (int16 -7)) "beq\t$a0, $s0, -0xc" 246 | assertDisasm (BEQ A0 S0 (int16 1)) "beq\t$a0, $s0, 0x4" 247 | assertDisasm (BEQ A0 S0 (int16 3)) "beq\t$a0, $s0, 0x8" 248 | 249 | [] 250 | let ``Use aliases`` () = 251 | let assertNoAliasDisasm (instr: uint) (expected: string) = 252 | let actual = disassembleInstr instr 0u Map.empty Flags.None 253 | Assert.Equal(expected, actual) 254 | let assertAliasDisasm (instr: uint) (expectedNoAlias: string) (expectedAlias: string) = 255 | assertNoAliasDisasm instr expectedNoAlias 256 | assertDisasm instr expectedAlias 257 | 258 | assertAliasDisasm NOP "sll\t$zero, $zero, 0" "nop" 259 | assertAliasDisasm (MOVE V0 A0) "or\t$v0, $a0, $zero" "move\t$v0, $a0" 260 | assertAliasDisasm (NEG V0 A0) "sub\t$v0, $zero, $a0" "neg\t$v0, $a0" 261 | assertAliasDisasm (NEGU V0 A0) "subu\t$v0, $zero, $a0" "negu\t$v0, $a0" 262 | assertAliasDisasm (LIU S0 (uint16 50000)) "ori\t$s0, $zero, 0xc350" "li\t$s0, 50000" 263 | assertAliasDisasm (BEQ S0 ZERO (int16 1)) "beq\t$s0, $zero, 0x4" "beqz\t$s0, 0x4" 264 | assertAliasDisasm (BNE S0 ZERO (int16 1)) "bne\t$s0, $zero, 0x4" "bnez\t$s0, 0x4" 265 | 266 | assertNoAliasDisasm (LI S0 (int16 50000)) "addiu\t$s0, $zero, 50000" 267 | assertDisasm (LI S0 (int16 -5000)) "li\t$s0, -5000" 268 | 269 | [] 270 | let ``Analyze branches and disassemble`` () = 271 | let data = [| 272 | NOP 273 | BLEZ A0 (int16 -1) 274 | |] 275 | let disasm = 276 | (disassembleData data 0x80010000u Map.empty Flags.UseAlias) 277 | |> String.concat "\n" 278 | Assert.Equal("\tnop\n\nloc_80010004:\n\tblez\t$a0, loc_80010004", disasm) 279 | 280 | [] 281 | let ``Analyze jumps and disassemble`` () = 282 | let data = [| 283 | NOP 284 | J (0x80010000u >>> 2) 285 | |] 286 | let disasm = 287 | (disassembleData data 0x80010000u Map.empty Flags.UseAlias) 288 | |> String.concat "\n" 289 | Assert.Equal("\nsub_80010000:\n\tnop\n\tj\tsub_80010000", disasm) 290 | 291 | [] 292 | let ``Simplified Load Immediate alias`` () = 293 | let assertLi op expected = 294 | let data = [|LUI V0 (uint16 0x1234); op|] 295 | let disasm = 296 | (disassembleData data 0u Map.empty Flags.UseAlias) 297 | |> String.concat "\n" 298 | Assert.Equal(expected, disasm) 299 | assertLi (ORI ZERO V0 (uint16 0x0678)) "\tli\t$v0, 0x12340678" 300 | assertLi (ORI V0 ZERO (uint16 0x0678)) "\tli\t$v0, 0x12340678" 301 | assertLi (ORI V0 V0 (uint16 0x0678)) "\tli\t$v0, 0x12340678" 302 | assertLi (ORI ZERO V1 (uint16 0x5678)) "\tlui\t$v0, 0x1234\n\tori\t$zero, $v1, 0x5678" 303 | assertLi (ORI V1 ZERO (uint16 0x5678)) "\tlui\t$v0, 0x1234\n\tli\t$v1, 0x5678" 304 | assertLi (ORI V1 V1 (uint16 0x5678)) "\tlui\t$v0, 0x1234\n\tori\t$v1, $v1, 0x5678" 305 | 306 | [] 307 | let ``Do not simplify to Load Immediate when a label is between the two instructions`` () = 308 | let data = [| 309 | BNE A0 A1 (int16 1) 310 | LUI V0 (uint16 0x1234) 311 | ORI V0 V0 (uint16 0x0678) 312 | |] 313 | let disasm = 314 | (disassembleData data 0x80010000u Map.empty Flags.UseAlias) 315 | |> String.concat "\n" 316 | let expected = [| 317 | "\tbne\t$a0, $a1, loc_80010008" 318 | "\tlui\t$v0, 0x1234" 319 | "" 320 | "loc_80010008:" 321 | "\tori\t$v0, $v0, 0x678" 322 | |] 323 | Assert.Equal(expected |> String.concat "\n", disasm) 324 | 325 | [] 326 | let ``Simplified Load/Save Byte/Half/Word`` () = 327 | let labels = [|(0x80034444u, "dword_80034444")|] |> Map.ofArray 328 | let assertPointer op expected = 329 | let data = [|LUI V0 (uint16 0x8003); op|] 330 | let disasm = 331 | (disassembleData data 0u labels Flags.UseAlias) 332 | |> String.concat "\n" 333 | Assert.Equal(expected, disasm) 334 | assertPointer (LW A0 V1 (int16 +0x1234)) "\tlui\t$v0, 0x8003\n\tlw\t$a0, 0x1234($v1)" 335 | assertPointer (LW A0 V0 (int16 +0x1234)) "\tlw\t$a0, 0x80031234" 336 | assertPointer (LW A0 V0 (int16 -0x2c60)) "\tlw\t$a0, 0x8002d3a0" 337 | assertPointer (LW A0 V0 (int16 +0x4444)) "\tlw\t$a0, dword_80034444" 338 | assertPointer (LB A0 V0 (int16 +0x4444)) "\tlb\t$a0, dword_80034444" 339 | assertPointer (LH A0 V0 (int16 +0x4444)) "\tlh\t$a0, dword_80034444" 340 | assertPointer (SB A0 V0 (int16 +0x4444)) "\tsb\t$a0, dword_80034444" 341 | assertPointer (SH A0 V0 (int16 +0x4444)) "\tsh\t$a0, dword_80034444" 342 | assertPointer (SW A0 V0 (int16 +0x4444)) "\tsw\t$a0, dword_80034444" 343 | 344 | [] 345 | let ``Unknown instructions`` () = 346 | assertDisasm 0xffffffffu ".word 0xffffffff" 347 | assertDisasm 0x45584520u ".word 0x45584520" 348 | assertDisasm 0x000a2964u ".word 0x000a2964" 349 | assertDisasm 0x3d657571u ".word 0x3d657571" 350 | assertDisasm 0x44547465u ".word 0x44547465" 351 | 352 | [] 353 | [] 354 | [] 355 | [] 356 | [] 357 | [] 358 | [] 359 | [] 360 | [] 361 | [] 362 | [] 363 | [] 364 | [] 365 | [] 366 | [] 367 | [] 368 | [] 369 | let ``Convert digit as a string`` (value: int) (expected: string) = 370 | Assert.Equal(expected, intsToString value) 371 | Assert.Equal($"-{expected}", intsToString -value) -------------------------------------------------------------------------------- /mipsdump.test/mipsdump.test.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | all 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /mipsdump/Disassembler.fs: -------------------------------------------------------------------------------- 1 | // DO NOT MODIFY THE COMMENTED LINES BELOW 2 | // MIT License 3 | // Copyright (c) 2022 Luciano Ciccariello 4 | // https://github.com/Xeeynamo/OpenKh/blob/master/LICENSE 5 | 6 | module Disassembler 7 | 8 | open Instructions 9 | open System 10 | open System.IO 11 | 12 | [] 13 | type Flags = 14 | | None = 0 15 | | UseAlias = 1 16 | | NoBranchAnalysis = 2 17 | 18 | let intsToString (n: int) = 19 | let mightBeDecimal = abs n < 16 || (n % 5) = 0 || (n % 3) = 0 20 | if mightBeDecimal then n.ToString() else 21 | if n >= 0 then $"0x{n:X}" else $"-0x{-n:X}" 22 | let intuToString (n: uint) = 23 | let mightBeDecimal = n < 16u || (n % 5u) = 0u || (n % 3u) = 0u 24 | if mightBeDecimal then n.ToString() else $"0x{n:X}" 25 | let imms instr = int (int16 (instr &&& 0xffffu)) 26 | let immu instr = uint (uint16 (instr &&& 0xffffu)) 27 | let op instr = instr >>> 26 |> int |> enum 28 | let rs instr = (instr >>> 21) &&& 0x1Fu |> int |> enum 29 | let rt instr = (instr >>> 16) &&& 0x1Fu |> int |> enum 30 | let strlow (value: Enum) = (string value).ToLower() 31 | 32 | let disassembleInstr (instr: uint) (addr: uint) (labels: Map) (flags: Flags) = 33 | let unkInstr instr = $".word 0x{instr:x08}" 34 | let hexs (value:int) = if value >= 0 then $"0x{value:x}" else $"-0x{-value:x}" 35 | let hexp (value:int) = if value = 0 then "" else hexs value 36 | let ofBranch (imm: int) = 37 | let relAddr = (imm + 1) <<< 2 38 | let absAddr = addr + (uint relAddr) 39 | match labels.TryGetValue absAddr with 40 | | true, label -> label 41 | | false, _ -> hexs ((imm + 1) <<< 1) 42 | let ofLabel addr = 43 | match labels.TryGetValue(addr) with 44 | | true, label -> label 45 | | false, _ -> $"0x{addr:x08}" 46 | let op = op instr 47 | let rs = rs instr 48 | let rt = rt instr 49 | let useAlias = flags.HasFlag(Flags.UseAlias) 50 | 51 | let special instr = 52 | let rd = (instr >>> 11) &&& 0x1Fu |> int |> enum 53 | let funct = instr &&& 0x3Fu |> int |> enum 54 | let shamt = (instr >>> 6) &&& 0x1Fu |> int 55 | 56 | match (funct, rd, rs, rt, shamt) with 57 | | Special.SLL, Reg.ZERO, Reg.ZERO, Reg.ZERO, 0 when useAlias -> "nop" 58 | | Special.SUB, _, Reg.ZERO, _, 0 when useAlias = true -> $"neg\t${strlow rd}, ${strlow rt}" 59 | | Special.SUBU, _, Reg.ZERO, _, 0 when useAlias = true -> $"negu\t${strlow rd}, ${strlow rt}" 60 | | Special.OR, _, _, Reg.ZERO, 0 when useAlias = true -> $"move\t${strlow rd}, ${strlow rs}" 61 | | Special.MFHI, _, Reg.ZERO, Reg.ZERO, 0 62 | | Special.MTHI, _, Reg.ZERO, Reg.ZERO, 0 63 | | Special.MFLO, _, Reg.ZERO, Reg.ZERO, 0 64 | | Special.MTLO, _, Reg.ZERO, Reg.ZERO, 0 -> $"{strlow funct}\t${strlow rd}" 65 | | Special.MULT, Reg.ZERO, _, _, 0 66 | | Special.MULTU, Reg.ZERO, _, _, 0 -> $"{strlow funct}\t${strlow rs}, ${strlow rt}" 67 | | Special.DIV, Reg.ZERO, _, _, 0 68 | | Special.DIVU, Reg.ZERO, _, _, 0 -> $"{strlow funct}\t$zero, ${strlow rs}, ${strlow rt}" 69 | | Special.SLL, _, Reg.ZERO, _, _ 70 | | Special.SRL, _, Reg.ZERO, _, _ 71 | | Special.SRA, _, Reg.ZERO, _, _ -> $"{strlow funct}\t${strlow rd}, ${strlow rt}, {shamt}" 72 | | Special.ADD, _, _, _, 0 73 | | Special.SUB, _, _, _, 0 74 | | Special.ADDU, _, _, _, 0 75 | | Special.SUBU, _, _, _, 0 76 | | Special.AND, _, _, _, 0 77 | | Special.OR, _, _, _, 0 78 | | Special.XOR, _, _, _, 0 79 | | Special.NOR, _, _, _, 0 80 | | Special.SLT, _, _, _, 0 81 | | Special.SLTU, _, _, _, 0 -> $"{strlow funct}\t${strlow rd}, ${strlow rs}, ${strlow rt}" 82 | | Special.SLLV, _, _, _, 0 83 | | Special.SRAV, _, _, _, 0 84 | | Special.SRLV, _, _, _, 0 -> $"{strlow funct}\t${strlow rd}, ${strlow rt}, ${strlow rs}" 85 | | Special.JR, Reg.ZERO, _, Reg.ZERO, 0 86 | | Special.JALR, Reg.ZERO, _, Reg.ZERO, 0 -> $"{strlow funct}\t${strlow rs}" 87 | | Special.SYSCALL, _, _, _, _ -> $"syscall\t0x{instr >>> 6:X}" 88 | | Special.BREAK, _, _, _, _ -> $"break\t0x{(instr >>> 16) &&& 0x3ffu:x}, 0x{(instr >>> 6) &&& 0x3ffu:x}" 89 | | _, _, _, _, _ -> unkInstr instr 90 | let regimm instr = 91 | let regimm = rt |> int |> enum 92 | match regimm with 93 | | Regimm.BLTZ | Regimm.BGEZ | Regimm.BLTZAL | Regimm.BGEZAL -> 94 | $"{strlow regimm}\t${strlow rs}, {ofBranch (imms instr)}" 95 | | _ -> unkInstr instr 96 | let cop instr = 97 | let cop = uint op &&& 3u 98 | let zzz = instr &&& 0x7ffu 99 | match (rs |> int |> enum, zzz) with 100 | | Cop.MFC, 0u -> $"mfc{cop}\t${strlow rt}, ${(instr >>> 11) &&& 0x1Fu}" 101 | | Cop.MTC, 0u -> $"mtc{cop}\t${strlow rt}, ${(instr >>> 11) &&& 0x1Fu}" 102 | | Cop.CFC, 0u -> $"cfc{cop}\t${strlow rt}, ${(instr >>> 11) &&& 0x1Fu}" 103 | | Cop.CTC, 0u -> $"ctc{cop}\t${strlow rt}, ${(instr >>> 11) &&& 0x1Fu}" 104 | | _ -> 105 | if (instr &&& (1u <<< 25)) <> 0u 106 | then $"cop{cop}\t0x{instr &&& 0x1FFFFFFu:x}" 107 | else unkInstr instr 108 | 109 | match (op, rs, rt) with 110 | | Op.SPECIAL, _, _ -> special instr 111 | | Op.REGIMM, _, _ -> regimm instr 112 | | Op.BEQ, _, Reg.ZERO 113 | | Op.BNE, _, Reg.ZERO when useAlias = true -> $"{strlow op}z\t${strlow rs}, {ofBranch (imms instr)}" 114 | | Op.BEQ, _, _ 115 | | Op.BNE, _, _ -> $"{strlow op}\t${strlow rs}, ${strlow rt}, {ofBranch (imms instr)}" 116 | | Op.BLEZ, _, Reg.ZERO 117 | | Op.BGTZ, _, Reg.ZERO -> $"{strlow op}\t${strlow rs}, {ofBranch (imms instr)}" 118 | | Op.LUI, Reg.ZERO, _ -> $"lui\t${strlow rt}, 0x{immu instr:X}" 119 | | Op.ADDIU, Reg.ZERO, _ when useAlias = true -> $"li\t${strlow rt}, {intsToString (imms instr)}" 120 | | Op.ORI, Reg.ZERO, _ when useAlias = true -> $"li\t${strlow rt}, {intuToString (immu instr)}" 121 | | Op.ADDI, _, _ 122 | | Op.SLTI, _, _ -> $"{strlow op}\t${strlow rt}, ${strlow rs}, {intsToString (imms instr)}" 123 | | Op.ADDIU, _, _ 124 | | Op.SLTIU, _, _ -> $"{strlow op}\t${strlow rt}, ${strlow rs}, {intuToString (immu instr)}" 125 | | Op.ANDI, _, _ 126 | | Op.ORI, _, _ 127 | | Op.XORI, _, _ -> $"{strlow op}\t${strlow rt}, ${strlow rs}, 0x{immu instr:x}" 128 | | Op.LB, _, _ | Op.LH, _, _ | Op.LWL, _, _ | Op.LW, _, _ 129 | | Op.LBU, _, _ | Op.LHU, _, _ | Op.LWR, _, _ 130 | | Op.SB, _, _ | Op.SH, _, _ | Op.SWL, _, _ | Op.SW, _, _ | Op.SWR, _, _ -> 131 | $"{strlow op}\t${strlow rt}, {hexp (imms instr)}(${strlow rs})" 132 | | Op.J, _, _ 133 | | Op.JAL, _, _ -> $"{strlow op}\t{ofLabel (((instr &&& 0x3FFFFFFu) <<< 2) ||| 0x80000000u)}" 134 | | Op.LWC0, _, _ | Op.LWC1, _, _ | Op.LWC2, _, _ | Op.LWC3, _, _ 135 | | Op.SWC0, _, _ | Op.SWC1, _, _ | Op.SWC2, _, _ | Op.SWC3, _, _ -> 136 | $"{strlow op}\t${int rt}, {hexp (imms instr)}(${strlow rs})" 137 | | Op.C0, _, _ | Op.C1, _, _ | Op.C2, _, _ | Op.C3, _, _ -> cop instr 138 | | _ -> unkInstr instr 139 | 140 | let (|IsAlias|_|) (instrs: uint[], index: int, addr: uint, labels: Map) = 141 | if index + 1 >= instrs.Length then None else 142 | if labels.ContainsKey(addr + 4u) then None else 143 | if op instrs[index] <> Op.LUI then None else 144 | let rtLui = rt instrs[index] 145 | let instr = instrs[index + 1] 146 | let rt = rt instr 147 | let rs = rs instr 148 | let op = op instr 149 | match op with 150 | | Op.ORI -> 151 | if (rt = Reg.ZERO && rtLui = rs) || (rs = Reg.ZERO && rtLui = rt) || (rtLui = rt && rt = rs) 152 | then Some $"li\t${strlow rtLui}, 0x{immu instrs[index]:x}{immu instr:x04}" 153 | else None 154 | | Op.LB | Op.LH | Op.LW | Op.SB | Op.SH | Op.SW -> 155 | if rs <> rtLui || rtLui = Reg.AT then None else 156 | let imm = uint ((int (immu instrs[index]) <<< 16) + (imms instr)) 157 | let disasm = 158 | match labels.TryGetValue imm with 159 | | true, label -> $"{strlow op}\t${strlow rt}, {label}" 160 | | false, _ -> $"{strlow op}\t${strlow rt}, 0x{imm:x}" 161 | Some disasm 162 | | _ -> None 163 | 164 | let rec disassembleInternal (instrs: uint[]) (index: int) (addr: uint32) (labels: Map) (flags: Flags) = 165 | seq { 166 | match labels.TryGetValue addr with 167 | | true, label -> yield $"\n{label}:" 168 | | false, _ -> () 169 | 170 | if index >= instrs.Length then () else 171 | match (instrs, index, addr, labels) with 172 | | IsAlias disasm -> 173 | yield $"\t{disasm}" 174 | yield! disassembleInternal instrs (index + 2) (addr + 8u) labels flags 175 | | _ -> 176 | yield $"\t{disassembleInstr instrs[index] addr labels flags}" 177 | yield! disassembleInternal instrs (index + 1) (addr + 4u) labels flags 178 | } 179 | 180 | let analyzeBranches (instrs: uint[]) (baseAddr: uint) (labels: Map) = 181 | let addLabel instr addr (labels: Map) = 182 | let relAddr = (imms instr + 1) <<< 2 183 | let absAddr = uint (addr + (uint relAddr)) 184 | if labels.ContainsKey(absAddr) = false then 185 | labels.Add(uint absAddr, $"loc_{absAddr:x}") 186 | else labels 187 | let addJumpLabel instr max (labels: Map) = 188 | let absAddr = uint ((instr &&& 0x3FFFFFFu) <<< 2) ||| 0x80000000u 189 | if absAddr < max && labels.ContainsKey(absAddr) = false then 190 | labels.Add(uint absAddr, $"sub_{absAddr:x}") 191 | else labels 192 | 193 | let maxAddr = baseAddr + uint instrs.Length * 4u 194 | let analyze labels (addr, instr) = 195 | let op = instr >>> 26 |> int |> enum 196 | match op with 197 | | Op.BEQ | Op.BNE | Op.BLEZ | Op.BGTZ -> addLabel instr addr labels 198 | | Op.REGIMM -> 199 | let regimm = (instr >>> 16) &&& 0x1Fu |> int |> enum 200 | match regimm with 201 | | Regimm.BLTZ | Regimm.BGEZ | Regimm.BLTZAL | Regimm.BGEZAL -> addLabel instr addr labels 202 | | _ -> labels 203 | | Op.J | Op.JAL -> addJumpLabel instr maxAddr labels 204 | | _ -> labels 205 | instrs |> Array.mapi (fun i instr -> (baseAddr + uint i * 4u, instr)) |> Array.fold analyze labels 206 | 207 | let disassembleData (instrs: uint[]) (baseAddr: uint32) (labels: Map) (flags: Flags) = 208 | let moreLabels = 209 | if flags.HasFlag(Flags.NoBranchAnalysis) 210 | then labels 211 | else analyzeBranches instrs baseAddr labels 212 | disassembleInternal instrs 0 baseAddr moreLabels flags 213 | 214 | let disassembleStream (reader: BinaryReader) (instrCount: int) (baseAddr: uint32) (labels: Map) (flags: Flags) = 215 | disassembleData (Array.init instrCount (fun _ -> reader.ReadUInt32())) baseAddr labels flags 216 | 217 | let disassembleStreamRange (reader: BinaryReader) (offsetStart: uint) (offsetEnd: uint) (baseAddr: uint32) (labels: Map) (flags: Flags) = 218 | let s = reader.BaseStream.Seek(int64 offsetStart, SeekOrigin.Begin) 219 | let e = (min (int64 offsetEnd) reader.BaseStream.Length) 220 | disassembleStream reader (int (e - s) / 4) (baseAddr + offsetStart) labels flags 221 | -------------------------------------------------------------------------------- /mipsdump/Idc.fs: -------------------------------------------------------------------------------- 1 | module Idc 2 | 3 | open System 4 | open System.Globalization 5 | 6 | let (|Hexadecimal|Decimal|Unknown|) (str: string) = 7 | let isHexDigit (ch: char) = Char.IsDigit(ch) || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f') 8 | if str.Length > 2 && str[..1].ToLower() = "0x" then 9 | if str[2..] |> String.forall(isHexDigit) then 10 | Hexadecimal str[2..] 11 | else 12 | Unknown 13 | else 14 | if str |> String.forall(Char.IsDigit) then 15 | Decimal 16 | else 17 | Unknown 18 | 19 | let asDigit (str:string) = 20 | match str with 21 | | Hexadecimal hexStr -> Some (UInt32.Parse (hexStr, NumberStyles.HexNumber)) 22 | | Decimal -> Some (UInt32.Parse str) 23 | | Unknown -> None 24 | 25 | let asString (str:string) = 26 | if str.Length >= 2 && str[0] = '"' && str[^0] = '"' then 27 | Some str[1..^1] 28 | else 29 | None 30 | 31 | let parseLabels (content:string[]) = 32 | let (|SetName|_|) (tokens:string[]) = 33 | if tokens.Length < 3 then None else 34 | match (tokens[0], asDigit tokens[1], asString tokens[2]) with 35 | | "set_name", Some addr, Some label -> Some (addr, label) 36 | | _, _, _ -> None 37 | let (|CreateData|_|) (tokens:string[]) = 38 | if tokens.Length < 2 then None else 39 | match (tokens[0], asDigit tokens[1]) with 40 | | "create_dword", Some addr -> Some (addr, $"word_{addr:x08}") 41 | | "create_word", Some addr -> Some (addr, $"half_{addr:x08}") 42 | | "create_byte", Some addr -> Some (addr, $"byte_{addr:x08}") 43 | | "make_array", Some addr -> Some (addr, $"array_{addr:x08}") 44 | | "create_strlit", Some addr -> Some (addr, $"asc_{addr:x08}") 45 | | _, _ -> None 46 | let parseIdcLine (line:string) = 47 | let tokens = 48 | line.Split('\t', ' ', ',', '(', ')', ';') 49 | |> Array.filter(fun token -> token.Length > 0) 50 | match tokens with 51 | | SetName (addr, label) -> Some (addr, label) 52 | | CreateData (addr, label) -> Some (addr, label) 53 | | _ -> None 54 | content |> Array.choose parseIdcLine |> Map.ofArray 55 | -------------------------------------------------------------------------------- /mipsdump/Instructions.fs: -------------------------------------------------------------------------------- 1 | // DO NOT MODIFY THE COMMENTED LINES BELOW 2 | // MIT License 3 | // Copyright (c) 2022 Luciano Ciccariello 4 | // https://github.com/Xeeynamo/OpenKh/blob/master/LICENSE 5 | 6 | module public Instructions 7 | 8 | type Reg = 9 | | ZERO = 0x00 | AT = 0x01 | V0 = 0x02 | V1 = 0x03 10 | | A0 = 0x04 | A1 = 0x05 | A2 = 0x06 | A3 = 0x07 11 | | T0 = 0x08 | T1 = 0x09 | T2 = 0x0A | T3 = 0x0B 12 | | T4 = 0x0C | T5 = 0x0D | T6 = 0x0E | T7 = 0x0F 13 | | S0 = 0x10 | S1 = 0x11 | S2 = 0x12 | S3 = 0x13 14 | | S4 = 0x14 | S5 = 0x15 | S6 = 0x16 | S7 = 0x17 15 | | T8 = 0x18 | T9 = 0x19 | K0 = 0x1A | K1 = 0x1B 16 | | GP = 0x1C | SP = 0x1D | FP = 0x1E | RA = 0x1F 17 | type Regimm = 18 | | BLTZ = 0x00 | BGEZ = 0x01 | BLTZAL = 0x10 | BGEZAL = 0x11 19 | type Op = 20 | | SPECIAL = 0x00 | REGIMM = 0x01 | J = 0x02 | JAL = 0x03 21 | | BEQ = 0x04 | BNE = 0x05 | BLEZ = 0x06 | BGTZ = 0x07 22 | | ADDI = 0x08 | ADDIU = 0x09 | SLTI = 0x0A | SLTIU = 0x0B 23 | | ANDI = 0x0C | ORI = 0x0D | XORI = 0x0E | LUI = 0x0F 24 | | C0 = 0x10 | C1 = 0x11 | C2 = 0x12 | C3 = 0x13 25 | | LB = 0x20 | LH = 0x21 | LWL = 0x22 | LW = 0x23 26 | | LBU = 0x24 | LHU = 0x25 | LWR = 0x26 27 | | SB = 0x28 | SH = 0x29 | SWL = 0x2a | SW = 0x2b 28 | | SWR = 0x2e 29 | | LWC0 = 0x30 | LWC1 = 0x31 | LWC2 = 0x32 | LWC3 = 0x33 30 | | SWC0 = 0x38 | SWC1 = 0x39 | SWC2 = 0x3a | SWC3 = 0x3b 31 | type Special = 32 | | SLL = 0x00 | SRL = 0x02 | SRA = 0x03 33 | | SLLV = 0x04 | SRLV = 0x06 | SRAV = 0x07 34 | | JR = 0x08 | JALR = 0x09 35 | | SYSCALL = 0x0C | BREAK = 0x0D 36 | | MFHI = 0x10 | MTHI = 0x11 | MFLO = 0x12 | MTLO = 0x13 37 | | MULT = 0x18 | MULTU = 0x19 | DIV = 0x1A | DIVU = 0x1B 38 | | ADD = 0x20 | ADDU = 0x21 | SUB = 0x22 | SUBU = 0x23 39 | | AND = 0x24 | OR = 0x25 | XOR = 0x26 | NOR = 0x27 40 | | SLT = 0x2A | SLTU = 0x2B 41 | type Cop = 42 | | MFC = 0x00 | CFC = 0x02 | MTC = 0x04 | CTC = 0x06 | BC = 0x10 43 | 44 | let s16to32 (value:int16) = (value |> uint) &&& 0xFFFFu 45 | let u16to32 (value:uint16) = (value |> uint) &&& 0xFFFFu 46 | 47 | // R-type instructions 48 | let SLL (dst:Reg) (src:Reg) (value:uint) = uint Special.SLL ||| (uint dst <<< 11) ||| (uint src <<< 16) ||| ((value &&& 0x1Fu) <<< 6) 49 | let SRL (dst:Reg) (src:Reg) (value:uint) = uint Special.SRL ||| (uint dst <<< 11) ||| (uint src <<< 16) ||| ((value &&& 0x1Fu) <<< 6) 50 | let SRA (dst:Reg) (src:Reg) (value:uint) = uint Special.SRA ||| (uint dst <<< 11) ||| (uint src <<< 16) ||| ((value &&& 0x1Fu) <<< 6) 51 | let SLLV (dst:Reg) (left:Reg) (right:Reg) = uint Special.SLLV ||| (uint dst <<< 11) ||| (uint left <<< 16) ||| (uint right <<< 21) 52 | let SRAV (dst:Reg) (left:Reg) (right:Reg) = uint Special.SRAV ||| (uint dst <<< 11) ||| (uint left <<< 16) ||| (uint right <<< 21) 53 | let SRLV (dst:Reg) (left:Reg) (right:Reg) = uint Special.SRLV ||| (uint dst <<< 11) ||| (uint left <<< 16) ||| (uint right <<< 21) 54 | let JR (reg:Reg) = uint Special.JR ||| (uint (reg:Reg) <<< 21) 55 | let JALR (reg:Reg) = uint Special.JALR ||| (uint (reg:Reg) <<< 21) 56 | let SYSCALL (code:uint) = uint Special.SYSCALL ||| ((code &&& 0xffffffu) <<< 6) 57 | let BREAK (code1:uint) (code2:uint) = uint Special.BREAK ||| ((code1 &&& 0x3ffu) <<< 16) ||| ((code2 &&& 0x3ffu) <<< 6) 58 | let MFHI (reg:Reg) = uint Special.MFHI ||| (uint (reg:Reg) <<< 11) 59 | let MTHI (reg:Reg) = uint Special.MTHI ||| (uint (reg:Reg) <<< 11) 60 | let MFLO (reg:Reg) = uint Special.MFLO ||| (uint (reg:Reg) <<< 11) 61 | let MTLO (reg:Reg) = uint Special.MTLO ||| (uint (reg:Reg) <<< 11) 62 | let MULT (dst:Reg) (src:Reg) = uint Special.MULT ||| (uint src <<< 16) ||| (uint dst <<< 21) 63 | let MULTU (dst:Reg) (src:Reg) = uint Special.MULTU ||| (uint src <<< 16) ||| (uint dst <<< 21) 64 | let DIV (dst:Reg) (src:Reg) = uint Special.DIV ||| (uint src <<< 16) ||| (uint dst <<< 21) 65 | let DIVU (dst:Reg) (src:Reg) = uint Special.DIVU ||| (uint src <<< 16) ||| (uint dst <<< 21) 66 | let ADD (dst:Reg) (left:Reg) (right:Reg) = uint Special.ADD ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 67 | let ADDU (dst:Reg) (left:Reg) (right:Reg) = uint Special.ADDU ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 68 | let SUB (dst:Reg) (left:Reg) (right:Reg) = uint Special.SUB ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 69 | let SUBU (dst:Reg) (left:Reg) (right:Reg) = uint Special.SUBU ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 70 | let AND (dst:Reg) (left:Reg) (right:Reg) = uint Special.AND ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 71 | let OR (dst:Reg) (left:Reg) (right:Reg) = uint Special.OR ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 72 | let XOR (dst:Reg) (left:Reg) (right:Reg) = uint Special.XOR ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 73 | let NOR (dst:Reg) (left:Reg) (right:Reg) = uint Special.NOR ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 74 | let SLT (dst:Reg) (left:Reg) (right:Reg) = uint Special.SLT ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 75 | let SLTU (dst:Reg) (left:Reg) (right:Reg) = uint Special.SLTU ||| (uint dst <<< 11) ||| (uint left <<< 21) ||| (uint right <<< 16) 76 | 77 | // Alias R-type instructions 78 | let NOP = SLL Reg.ZERO Reg.ZERO 0u 79 | let MOVE(dst:Reg) (src:Reg) = OR dst src Reg.ZERO 80 | let NEG(dst:Reg) (src:Reg) = SUB dst Reg.ZERO src 81 | let NEGU(dst:Reg) (src:Reg) = SUBU dst Reg.ZERO src 82 | 83 | // I-type instructions 84 | let BLTZ (reg:Reg) (imm:int16) = s16to32 imm ||| (uint Regimm.BLTZ <<< 16) ||| (uint reg <<< 21) ||| (uint Op.REGIMM <<< 26) 85 | let BGEZ (reg:Reg) (imm:int16) = s16to32 imm ||| (uint Regimm.BGEZ <<< 16) ||| (uint reg <<< 21) ||| (uint Op.REGIMM <<< 26) 86 | let BLTZAL (reg:Reg) (imm:int16) = s16to32 imm ||| (uint Regimm.BLTZAL <<< 16) ||| (uint reg <<< 21) ||| (uint Op.REGIMM <<< 26) 87 | let BGEZAL (reg:Reg) (imm:int16) = s16to32 imm ||| (uint Regimm.BGEZAL <<< 16) ||| (uint reg <<< 21) ||| (uint Op.REGIMM <<< 26) 88 | let BEQ (left:Reg) (right:Reg) (imm:int16) = s16to32 imm ||| (uint right <<< 16) ||| (uint left <<< 21) ||| (uint Op.BEQ <<< 26) 89 | let BNE (left:Reg) (right:Reg) (imm:int16) = s16to32 imm ||| (uint right <<< 16) ||| (uint left <<< 21) ||| (uint Op.BNE <<< 26) 90 | let BLEZ (reg:Reg) (imm:int16) = s16to32 imm ||| (uint reg <<< 21) ||| (uint Op.BLEZ <<< 26) 91 | let BGTZ (reg:Reg) (imm:int16) = s16to32 imm ||| (uint reg <<< 21) ||| (uint Op.BGTZ <<< 26) 92 | let ADDI (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.ADDI <<< 26) 93 | let ADDIU (dst:Reg) (src:Reg) (imm:uint16) = u16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.ADDIU <<< 26) 94 | let SLTI (left:Reg) (right:Reg) (imm:int16) = s16to32 imm ||| (uint left <<< 16) ||| (uint right <<< 21) ||| (uint Op.SLTI <<< 26) 95 | let SLTIU (left:Reg) (right:Reg) (imm:int16) = s16to32 imm ||| (uint left <<< 16) ||| (uint right <<< 21) ||| (uint Op.SLTIU <<< 26) 96 | let ANDI (dst:Reg) (src:Reg) (imm:uint16) = u16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.ANDI <<< 26) 97 | let ORI (dst:Reg) (src:Reg) (imm:uint16) = u16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.ORI <<< 26) 98 | let XORI (dst:Reg) (src:Reg) (imm:uint16) = u16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.XORI <<< 26) 99 | let LUI (dst:Reg) (imm:uint16) = u16to32 imm ||| (uint dst <<< 16) ||| (uint Op.LUI <<< 26) 100 | let LB (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LB <<< 26) 101 | let LH (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LH <<< 26) 102 | let LWL (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LWL <<< 26) 103 | let LW (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LW <<< 26) 104 | let LBU (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LBU <<< 26) 105 | let LHU (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LHU <<< 26) 106 | let LWR (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LWR <<< 26) 107 | let SB (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SB <<< 26) 108 | let SH (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SH <<< 26) 109 | let SWL (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SWL <<< 26) 110 | let SW (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SW <<< 26) 111 | let SWR (dst:Reg) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SWR <<< 26) 112 | 113 | // Alias I-type instructions 114 | let BAL (reg:Reg) (imm:int16) = BGEZAL reg imm 115 | let LI (dst:Reg) (imm:int16) = ADDIU dst Reg.ZERO (uint16 imm) 116 | let LIU (dst:Reg) (imm:uint16) = ORI dst Reg.ZERO (uint16 imm) 117 | 118 | // Co-processor instructions 119 | let MFC0 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.MFC <<< 21) ||| (uint Op.C0 <<< 26) 120 | let MFC1 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.MFC <<< 21) ||| (uint Op.C1 <<< 26) 121 | let MFC2 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.MFC <<< 21) ||| (uint Op.C2 <<< 26) 122 | let MFC3 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.MFC <<< 21) ||| (uint Op.C3 <<< 26) 123 | let MTC0 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.MTC <<< 21) ||| (uint Op.C0 <<< 26) 124 | let MTC1 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.MTC <<< 21) ||| (uint Op.C1 <<< 26) 125 | let MTC2 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.MTC <<< 21) ||| (uint Op.C2 <<< 26) 126 | let MTC3 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.MTC <<< 21) ||| (uint Op.C3 <<< 26) 127 | let CFC0 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.CFC <<< 21) ||| (uint Op.C0 <<< 26) 128 | let CFC1 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.CFC <<< 21) ||| (uint Op.C1 <<< 26) 129 | let CFC2 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.CFC <<< 21) ||| (uint Op.C2 <<< 26) 130 | let CFC3 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.CFC <<< 21) ||| (uint Op.C3 <<< 26) 131 | let CTC0 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.CTC <<< 21) ||| (uint Op.C0 <<< 26) 132 | let CTC1 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.CTC <<< 21) ||| (uint Op.C1 <<< 26) 133 | let CTC2 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.CTC <<< 21) ||| (uint Op.C2 <<< 26) 134 | let CTC3 (dst:Reg) (src:uint) = ((src &&& 0x1Fu) <<< 11) ||| (uint dst <<< 16) ||| (uint Cop.CTC <<< 21) ||| (uint Op.C3 <<< 26) 135 | let COP0 (offset:uint32) = (offset &&& 0x1FFFFFFu) ||| (1u <<< 25) ||| (uint Op.C0 <<< 26) 136 | let COP1 (offset:uint32) = (offset &&& 0x1FFFFFFu) ||| (1u <<< 25) ||| (uint Op.C1 <<< 26) 137 | let COP2 (offset:uint32) = (offset &&& 0x1FFFFFFu) ||| (1u <<< 25) ||| (uint Op.C2 <<< 26) 138 | let COP3 (offset:uint32) = (offset &&& 0x1FFFFFFu) ||| (1u <<< 25) ||| (uint Op.C3 <<< 26) 139 | let LWC0 (dst:uint) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LWC0 <<< 26) 140 | let LWC1 (dst:uint) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LWC1 <<< 26) 141 | let LWC2 (dst:uint) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LWC2 <<< 26) 142 | let LWC3 (dst:uint) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.LWC3 <<< 26) 143 | let SWC0 (dst:uint) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SWC0 <<< 26) 144 | let SWC1 (dst:uint) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SWC1 <<< 26) 145 | let SWC2 (dst:uint) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SWC2 <<< 26) 146 | let SWC3 (dst:uint) (src:Reg) (imm:int16) = s16to32 imm ||| (uint dst <<< 16) ||| (uint src <<< 21) ||| (uint Op.SWC3 <<< 26) 147 | let BC0F (offset:int16) = s16to32 offset ||| (0x10u <<< 26) ||| (0x10u <<< 20) ||| (0u <<< 16) 148 | let BC0T (offset:int16) = s16to32 offset ||| (0x10u <<< 26) ||| (0x10u <<< 20) ||| (1u <<< 16) 149 | let BC1F (offset:int16) = s16to32 offset ||| (0x11u <<< 26) ||| (0x10u <<< 20) ||| (0u <<< 16) 150 | let BC1T (offset:int16) = s16to32 offset ||| (0x11u <<< 26) ||| (0x10u <<< 20) ||| (1u <<< 16) 151 | let BC2F (offset:int16) = s16to32 offset ||| (0x12u <<< 26) ||| (0x10u <<< 20) ||| (0u <<< 16) 152 | let BC2T (offset:int16) = s16to32 offset ||| (0x12u <<< 26) ||| (0x10u <<< 20) ||| (1u <<< 16) 153 | let BC3F (offset:int16) = s16to32 offset ||| (0x13u <<< 26) ||| (0x10u <<< 20) ||| (0u <<< 16) 154 | let BC3T (offset:int16) = s16to32 offset ||| (0x13u <<< 26) ||| (0x10u <<< 20) ||| (1u <<< 16) 155 | 156 | // J-type instructions 157 | let J (addr:uint) = (addr &&& 0x3FFFFFFu) ||| (uint Op.J <<< 26) 158 | let JAL (addr:uint) = (addr &&& 0x3FFFFFFu) ||| (uint Op.JAL <<< 26) 159 | -------------------------------------------------------------------------------- /mipsdump/Program.fs: -------------------------------------------------------------------------------- 1 | open System 2 | open System.Globalization 3 | open System.IO 4 | open Argu 5 | open Disassembler 6 | 7 | type CliArguments = 8 | | [] [] [] Input of exePath:string 9 | | [] Output of outPath:string 10 | | [] Start of s:string 11 | | [] End of e:string 12 | | [] Idc of idcPath:string 13 | | [] Base_Address of baseAddress:string 14 | | [] Use_Alias 15 | | [] No_Branch_Analysis 16 | 17 | interface IArgParserTemplate with 18 | member s.Usage = 19 | match s with 20 | | Input _ -> "executable file compiled in MIPS machine code to disassemble" 21 | | Output _ -> "output file that will contain the disassembled code\nprints to stdout when not specified" 22 | | Start _ -> "start file offset in hexadecimal\nwhen not specified starts from the beginning of the file" 23 | | End _ -> "end file offset in hexadecimal\nwhen not specified then disassemble until the end of the file" 24 | | Idc _ -> "IDC file path from IDA Pro\nuseful if you have user-defined function labels" 25 | | Base_Address _ -> "virtual memory start address in hexadecimal\nfor PS1 executable it is recommended to specify a value of 8000F800\nby default it is assigned to 80010000" 26 | | Use_Alias _ -> "replace some instructions with aliases to make the disassembly easier to read" 27 | | No_Branch_Analysis _ -> "do not scan assembly to create labels\nonly used for fast-produced raw disassembly" 28 | 29 | [] 30 | let main argv = 31 | let parser = ArgumentParser.Create(programName = "mipsdump") 32 | let parseHex (s: string) = UInt32.Parse (s, NumberStyles.HexNumber) 33 | try 34 | let parsed = parser.ParseCommandLine(inputs = argv, raiseOnUsage = true) 35 | let exePath = parsed.GetResult Input 36 | use stream = File.OpenRead(exePath) 37 | let labels = 38 | if parsed.Contains Idc then 39 | let idcPath = parsed.GetResult Idc 40 | Idc.parseLabels (File.ReadAllLines(idcPath)) 41 | else Map.empty 42 | let flags = 43 | if parsed.Contains Use_Alias then Flags.UseAlias else Flags.None 44 | ||| if parsed.Contains No_Branch_Analysis then Flags.NoBranchAnalysis else Flags.None 45 | let baseAddress = if parsed.Contains Base_Address then (parseHex (parsed.GetResult Base_Address)) else 0x80010000u 46 | let s = if parsed.Contains Start then (parseHex (parsed.GetResult Start)) else 0u 47 | let e = if parsed.Contains End then (parseHex (parsed.GetResult Start)) else (uint stream.Length) 48 | 49 | use reader = new BinaryReader (stream) 50 | let disasm = Disassembler.disassembleStreamRange reader s e baseAddress labels flags 51 | if parsed.Contains Output then 52 | let outPath = parsed.GetResult Output 53 | File.WriteAllLines(outPath, disasm) 54 | else for line in disasm do printfn "%s" line 55 | 0 56 | with e -> 57 | eprintfn "%s" e.Message 58 | 1 59 | -------------------------------------------------------------------------------- /mipsdump/mipsdump.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | preview 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------