/ tests / print_info.cpp
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 = [&regs, &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)] = &regs[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"] = &regs[13];
209          name_map["lr"] = &regs[14];
210          name_map["pc"] = &regs[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  }