print_info.cpp
1 /* This file is part of the dynarmic project. 2 * Copyright (c) 2018 MerryMage 3 * SPDX-License-Identifier: 0BSD 4 */ 5 6 #include <algorithm> 7 #include <cctype> 8 #include <cstdlib> 9 #include <cstring> 10 #include <iostream> 11 #include <map> 12 #include <optional> 13 #include <string> 14 15 #include <fmt/format.h> 16 #include <fmt/ostream.h> 17 #include <mcl/bit/swap.hpp> 18 #include <mcl/stdint.hpp> 19 20 #include "dynarmic/common/llvm_disassemble.h" 21 #include "dynarmic/frontend/A32/a32_location_descriptor.h" 22 #include "dynarmic/frontend/A32/decoder/arm.h" 23 #include "dynarmic/frontend/A32/decoder/asimd.h" 24 #include "dynarmic/frontend/A32/decoder/vfp.h" 25 #include "dynarmic/frontend/A32/translate/a32_translate.h" 26 #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" 27 #include "dynarmic/frontend/A64/a64_location_descriptor.h" 28 #include "dynarmic/frontend/A64/decoder/a64.h" 29 #include "dynarmic/frontend/A64/translate/a64_translate.h" 30 #include "dynarmic/frontend/A64/translate/impl/impl.h" 31 #include "dynarmic/interface/A32/a32.h" 32 #include "dynarmic/interface/A32/disassembler.h" 33 #include "dynarmic/ir/basic_block.h" 34 #include "dynarmic/ir/opt/passes.h" 35 36 using namespace Dynarmic; 37 38 const char* GetNameOfA32Instruction(u32 instruction) { 39 if (auto vfp_decoder = A32::DecodeVFP<A32::TranslatorVisitor>(instruction)) { 40 return vfp_decoder->get().GetName(); 41 } else if (auto asimd_decoder = A32::DecodeASIMD<A32::TranslatorVisitor>(instruction)) { 42 return asimd_decoder->get().GetName(); 43 } else if (auto decoder = A32::DecodeArm<A32::TranslatorVisitor>(instruction)) { 44 return decoder->get().GetName(); 45 } 46 return "<null>"; 47 } 48 49 const char* GetNameOfA64Instruction(u32 instruction) { 50 if (auto decoder = A64::Decode<A64::TranslatorVisitor>(instruction)) { 51 return decoder->get().GetName(); 52 } 53 return "<null>"; 54 } 55 56 void PrintA32Instruction(u32 instruction) { 57 fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch32(false, 0, (u8*)&instruction, sizeof(instruction))); 58 fmt::print("Name: {}\n", GetNameOfA32Instruction(instruction)); 59 60 const A32::LocationDescriptor location{0, {}, {}}; 61 IR::Block ir_block{location}; 62 const bool should_continue = A32::TranslateSingleInstruction(ir_block, location, instruction); 63 fmt::print("should_continue: {}\n\n", should_continue); 64 65 Optimization::NamingPass(ir_block); 66 67 fmt::print("IR:\n"); 68 fmt::print("{}\n", IR::DumpBlock(ir_block)); 69 70 Optimization::A32GetSetElimination(ir_block, {}); 71 Optimization::DeadCodeElimination(ir_block); 72 Optimization::ConstantPropagation(ir_block); 73 Optimization::DeadCodeElimination(ir_block); 74 Optimization::IdentityRemovalPass(ir_block); 75 76 fmt::print("Optimized IR:\n"); 77 fmt::print("{}\n", IR::DumpBlock(ir_block)); 78 } 79 80 void PrintA64Instruction(u32 instruction) { 81 fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch64(instruction)); 82 fmt::print("Name: {}\n", GetNameOfA64Instruction(instruction)); 83 84 const A64::LocationDescriptor location{0, {}}; 85 IR::Block ir_block{location}; 86 const bool should_continue = A64::TranslateSingleInstruction(ir_block, location, instruction); 87 fmt::print("should_continue: {}\n\n", should_continue); 88 89 Optimization::NamingPass(ir_block); 90 91 fmt::print("IR:\n"); 92 fmt::print("{}\n", IR::DumpBlock(ir_block)); 93 94 Optimization::A64GetSetElimination(ir_block); 95 Optimization::DeadCodeElimination(ir_block); 96 Optimization::ConstantPropagation(ir_block); 97 Optimization::DeadCodeElimination(ir_block); 98 Optimization::IdentityRemovalPass(ir_block); 99 100 fmt::print("Optimized IR:\n"); 101 fmt::print("{}\n", IR::DumpBlock(ir_block)); 102 } 103 104 void PrintThumbInstruction(u32 instruction) { 105 const size_t inst_size = (instruction >> 16) == 0 ? 2 : 4; 106 if (inst_size == 4) 107 instruction = mcl::bit::swap_halves_32(instruction); 108 109 fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch32(true, 0, (u8*)&instruction, inst_size)); 110 111 const A32::LocationDescriptor location{0, A32::PSR{0x1F0}, {}}; 112 IR::Block ir_block{location}; 113 const bool should_continue = A32::TranslateSingleInstruction(ir_block, location, instruction); 114 fmt::print("should_continue: {}\n\n", should_continue); 115 116 Optimization::NamingPass(ir_block); 117 118 fmt::print("IR:\n"); 119 fmt::print("{}\n", IR::DumpBlock(ir_block)); 120 121 Optimization::A32GetSetElimination(ir_block, {}); 122 Optimization::DeadCodeElimination(ir_block); 123 Optimization::ConstantPropagation(ir_block); 124 Optimization::DeadCodeElimination(ir_block); 125 Optimization::IdentityRemovalPass(ir_block); 126 127 fmt::print("Optimized IR:\n"); 128 fmt::print("{}\n", IR::DumpBlock(ir_block)); 129 } 130 131 class ExecEnv final : public Dynarmic::A32::UserCallbacks { 132 public: 133 u64 ticks_left = 0; 134 std::map<u32, u8> memory; 135 136 std::uint8_t MemoryRead8(u32 vaddr) override { 137 if (auto iter = memory.find(vaddr); iter != memory.end()) { 138 return iter->second; 139 } 140 return 0; 141 } 142 std::uint16_t MemoryRead16(u32 vaddr) override { 143 return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; 144 } 145 std::uint32_t MemoryRead32(u32 vaddr) override { 146 return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; 147 } 148 std::uint64_t MemoryRead64(u32 vaddr) override { 149 return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; 150 } 151 152 void MemoryWrite8(u32 vaddr, std::uint8_t value) override { 153 memory[vaddr] = value; 154 } 155 void MemoryWrite16(u32 vaddr, std::uint16_t value) override { 156 MemoryWrite8(vaddr, static_cast<u8>(value)); 157 MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8)); 158 } 159 void MemoryWrite32(u32 vaddr, std::uint32_t value) override { 160 MemoryWrite16(vaddr, static_cast<u16>(value)); 161 MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16)); 162 } 163 void MemoryWrite64(u32 vaddr, std::uint64_t value) override { 164 MemoryWrite32(vaddr, static_cast<u32>(value)); 165 MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32)); 166 } 167 168 void InterpreterFallback(u32 pc, size_t num_instructions) override { 169 fmt::print("> InterpreterFallback({:08x}, {}) code = {:08x}\n", pc, num_instructions, *MemoryReadCode(pc)); 170 } 171 void CallSVC(std::uint32_t swi) override { 172 fmt::print("> CallSVC({})\n", swi); 173 } 174 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 175 fmt::print("> ExceptionRaised({:08x}, {})", pc, static_cast<size_t>(exception)); 176 } 177 178 void AddTicks(std::uint64_t ticks) override { 179 if (ticks > ticks_left) { 180 ticks_left = 0; 181 return; 182 } 183 ticks_left -= ticks; 184 } 185 std::uint64_t GetTicksRemaining() override { 186 return ticks_left; 187 } 188 }; 189 190 void ExecuteA32Instruction(u32 instruction) { 191 ExecEnv env; 192 A32::Jit cpu{A32::UserConfig{&env}}; 193 env.ticks_left = 1; 194 195 std::array<u32, 16> regs{}; 196 std::array<u32, 64> ext_regs{}; 197 u32 cpsr = 0; 198 u32 fpscr = 0; 199 200 const std::map<std::string, u32*> name_map = [®s, &ext_regs, &cpsr, &fpscr] { 201 std::map<std::string, u32*> name_map; 202 for (size_t i = 0; i < regs.size(); i++) { 203 name_map[fmt::format("r{}", i)] = ®s[i]; 204 } 205 for (size_t i = 0; i < ext_regs.size(); i++) { 206 name_map[fmt::format("s{}", i)] = &ext_regs[i]; 207 } 208 name_map["sp"] = ®s[13]; 209 name_map["lr"] = ®s[14]; 210 name_map["pc"] = ®s[15]; 211 name_map["cpsr"] = &cpsr; 212 name_map["fpscr"] = &fpscr; 213 return name_map; 214 }(); 215 216 const auto get_line = []() { 217 std::string line; 218 std::getline(std::cin, line); 219 std::transform(line.begin(), line.end(), line.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); }); 220 return line; 221 }; 222 223 const auto get_value = [&get_line]() -> std::optional<u32> { 224 std::string line = get_line(); 225 if (line.length() > 2 && line[0] == '0' && line[1] == 'x') 226 line = line.substr(2); 227 if (line.length() > 8) 228 return std::nullopt; 229 230 char* endptr; 231 const u32 value = strtol(line.c_str(), &endptr, 16); 232 if (line.c_str() + line.length() != endptr) 233 return std::nullopt; 234 235 return value; 236 }; 237 238 while (std::cin) { 239 fmt::print("register: "); 240 const std::string reg_name = get_line(); 241 if (const auto iter = name_map.find(reg_name); iter != name_map.end()) { 242 fmt::print("value: "); 243 if (const auto value = get_value()) { 244 *(iter->second) = *value; 245 fmt::print("> {} = 0x{:08x}\n", reg_name, *value); 246 } 247 } else if (reg_name == "mem" || reg_name == "memory") { 248 fmt::print("address: "); 249 if (const auto address = get_value()) { 250 fmt::print("value: "); 251 if (const auto value = get_value()) { 252 env.MemoryWrite32(*address, *value); 253 fmt::print("> mem[0x{:08x}] = 0x{:08x}\n", *address, *value); 254 } 255 } 256 } else if (reg_name == "end") { 257 break; 258 } 259 } 260 fmt::print("\n\n"); 261 262 cpu.Regs() = regs; 263 cpu.ExtRegs() = ext_regs; 264 cpu.SetCpsr(cpsr); 265 cpu.SetFpscr(fpscr); 266 267 const u32 initial_pc = regs[15]; 268 env.MemoryWrite32(initial_pc + 0, instruction); 269 env.MemoryWrite32(initial_pc + 4, 0xEAFFFFFE); // B +0 270 271 cpu.Run(); 272 273 fmt::print("Registers modified:\n"); 274 for (size_t i = 0; i < regs.size(); ++i) { 275 if (regs[i] != cpu.Regs()[i]) { 276 fmt::print("{:3s}: {:08x}\n", static_cast<A32::Reg>(i), cpu.Regs()[i]); 277 } 278 } 279 for (size_t i = 0; i < ext_regs.size(); ++i) { 280 if (ext_regs[i] != cpu.ExtRegs()[i]) { 281 fmt::print("{:3s}: {:08x}\n", static_cast<A32::ExtReg>(i), cpu.Regs()[i]); 282 } 283 } 284 if (cpsr != cpu.Cpsr()) { 285 fmt::print("cpsr {:08x}\n", cpu.Cpsr()); 286 } 287 if (fpscr != cpu.Fpscr()) { 288 fmt::print("fpscr{:08x}\n", cpu.Fpscr()); 289 } 290 fmt::print("Modified memory:\n"); 291 for (auto iter = env.memory.begin(); iter != env.memory.end(); ++iter) { 292 fmt::print("{:08x} {:02x}\n", iter->first, iter->second); 293 } 294 } 295 296 int main(int argc, char** argv) { 297 if (argc < 3 || argc > 4) { 298 fmt::print("usage: {} <a32/a64/thumb> <instruction_in_hex> [-exec]\n", argv[0]); 299 return 1; 300 } 301 302 const char* const hex_instruction = [argv] { 303 if (strlen(argv[2]) > 2 && argv[2][0] == '0' && argv[2][1] == 'x') { 304 return argv[2] + 2; 305 } 306 return argv[2]; 307 }(); 308 309 if (strlen(hex_instruction) > 8) { 310 fmt::print("hex string too long\n"); 311 return 1; 312 } 313 314 const u32 instruction = strtol(hex_instruction, nullptr, 16); 315 316 if (strcmp(argv[1], "a32") == 0) { 317 PrintA32Instruction(instruction); 318 } else if (strcmp(argv[1], "a64") == 0) { 319 PrintA64Instruction(instruction); 320 } else if (strcmp(argv[1], "t32") == 0 || strcmp(argv[1], "t16") == 0 || strcmp(argv[1], "thumb") == 0) { 321 PrintThumbInstruction(instruction); 322 } else { 323 fmt::print("Invalid mode: {}\nValid values: a32, a64, thumb\n", argv[1]); 324 return 1; 325 } 326 327 if (argc == 4) { 328 if (strcmp(argv[3], "-exec") != 0) { 329 fmt::print("Invalid option {}\n", argv[3]); 330 return 1; 331 } 332 333 if (strcmp(argv[1], "a32") == 0) { 334 ExecuteA32Instruction(instruction); 335 } else { 336 fmt::print("Executing in this mode not currently supported\n"); 337 return 1; 338 } 339 } 340 341 return 0; 342 }