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 = [®s, &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)] = ®s[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"] = ®s[13];
176 name_map["lr"] = ®s[14];
177 name_map["pc"] = ®s[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