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