1 /*
2 * Copyright (c) 2018-2019 Atmosphère-NX
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 /*
18 * Adapted by DarkLordZach for use/interaction with yuzu
19 *
20 * Modifications Copyright 2019 yuzu emulator team
21 * Licensed under GPLv2 or any later version
22 * Refer to the license.txt file included.
23 */
24
25 #include "common/assert.h"
26 #include "common/scope_exit.h"
27 #include "core/memory/dmnt_cheat_types.h"
28 #include "core/memory/dmnt_cheat_vm.h"
29
30 namespace Core::Memory {
31
DmntCheatVm(std::unique_ptr<Callbacks> callbacks)32 DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {}
33
34 DmntCheatVm::~DmntCheatVm() = default;
35
DebugLog(u32 log_id,u64 value)36 void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
37 callbacks->DebugLog(static_cast<u8>(log_id), value);
38 }
39
LogOpcode(const CheatVmOpcode & opcode)40 void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
41 if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) {
42 callbacks->CommandLog("Opcode: Store Static");
43 callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
44 callbacks->CommandLog(
45 fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type)));
46 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register));
47 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address));
48 callbacks->CommandLog(fmt::format("Value: {:X}", store_static->value.bit64));
49 } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) {
50 callbacks->CommandLog("Opcode: Begin Conditional");
51 callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width));
52 callbacks->CommandLog(
53 fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type)));
54 callbacks->CommandLog(
55 fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type)));
56 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address));
57 callbacks->CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64));
58 } else if (std::holds_alternative<EndConditionalOpcode>(opcode.opcode)) {
59 callbacks->CommandLog("Opcode: End Conditional");
60 } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) {
61 if (ctrl_loop->start_loop) {
62 callbacks->CommandLog("Opcode: Start Loop");
63 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
64 callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters));
65 } else {
66 callbacks->CommandLog("Opcode: End Loop");
67 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
68 }
69 } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) {
70 callbacks->CommandLog("Opcode: Load Register Static");
71 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index));
72 callbacks->CommandLog(fmt::format("Value: {:X}", ldr_static->value));
73 } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) {
74 callbacks->CommandLog("Opcode: Load Register Memory");
75 callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width));
76 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index));
77 callbacks->CommandLog(
78 fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type)));
79 callbacks->CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg));
80 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address));
81 } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) {
82 callbacks->CommandLog("Opcode: Store Static to Address");
83 callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width));
84 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index));
85 if (str_static->add_offset_reg) {
86 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
87 }
88 callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
89 callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value));
90 } else if (auto perform_math_static =
91 std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) {
92 callbacks->CommandLog("Opcode: Perform Static Arithmetic");
93 callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width));
94 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index));
95 callbacks->CommandLog(
96 fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type)));
97 callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
98 } else if (auto begin_keypress_cond =
99 std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) {
100 callbacks->CommandLog("Opcode: Begin Keypress Conditional");
101 callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
102 } else if (auto perform_math_reg =
103 std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) {
104 callbacks->CommandLog("Opcode: Perform Register Arithmetic");
105 callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width));
106 callbacks->CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index));
107 callbacks->CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index));
108 if (perform_math_reg->has_immediate) {
109 callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
110 } else {
111 callbacks->CommandLog(
112 fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
113 }
114 } else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) {
115 callbacks->CommandLog("Opcode: Store Register to Address");
116 callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width));
117 callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index));
118 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index));
119 callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg));
120 switch (str_register->ofs_type) {
121 case StoreRegisterOffsetType::None:
122 break;
123 case StoreRegisterOffsetType::Reg:
124 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
125 break;
126 case StoreRegisterOffsetType::Imm:
127 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
128 break;
129 case StoreRegisterOffsetType::MemReg:
130 callbacks->CommandLog(
131 fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
132 break;
133 case StoreRegisterOffsetType::MemImm:
134 case StoreRegisterOffsetType::MemImmReg:
135 callbacks->CommandLog(
136 fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
137 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
138 break;
139 }
140 } else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) {
141 callbacks->CommandLog("Opcode: Begin Register Conditional");
142 callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
143 callbacks->CommandLog(
144 fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type)));
145 callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
146 switch (begin_reg_cond->comp_type) {
147 case CompareRegisterValueType::StaticValue:
148 callbacks->CommandLog("Comp Type: Static Value");
149 callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
150 break;
151 case CompareRegisterValueType::OtherRegister:
152 callbacks->CommandLog("Comp Type: Other Register");
153 callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
154 break;
155 case CompareRegisterValueType::MemoryRelAddr:
156 callbacks->CommandLog("Comp Type: Memory Relative Address");
157 callbacks->CommandLog(
158 fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
159 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
160 break;
161 case CompareRegisterValueType::MemoryOfsReg:
162 callbacks->CommandLog("Comp Type: Memory Offset Register");
163 callbacks->CommandLog(
164 fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
165 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
166 break;
167 case CompareRegisterValueType::RegisterRelAddr:
168 callbacks->CommandLog("Comp Type: Register Relative Address");
169 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
170 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
171 break;
172 case CompareRegisterValueType::RegisterOfsReg:
173 callbacks->CommandLog("Comp Type: Register Offset Register");
174 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
175 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
176 break;
177 }
178 } else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) {
179 callbacks->CommandLog("Opcode: Save or Restore Register");
180 callbacks->CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index));
181 callbacks->CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index));
182 callbacks->CommandLog(
183 fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_reg->op_type)));
184 } else if (auto save_restore_regmask =
185 std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) {
186 callbacks->CommandLog("Opcode: Save or Restore Register Mask");
187 callbacks->CommandLog(
188 fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type)));
189 for (std::size_t i = 0; i < NumRegisters; i++) {
190 callbacks->CommandLog(
191 fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i]));
192 }
193 } else if (auto rw_static_reg = std::get_if<ReadWriteStaticRegisterOpcode>(&opcode.opcode)) {
194 callbacks->CommandLog("Opcode: Read/Write Static Register");
195 if (rw_static_reg->static_idx < NumReadableStaticRegisters) {
196 callbacks->CommandLog("Op Type: ReadStaticRegister");
197 } else {
198 callbacks->CommandLog("Op Type: WriteStaticRegister");
199 }
200 callbacks->CommandLog(fmt::format("Reg Idx {:X}", rw_static_reg->idx));
201 callbacks->CommandLog(fmt::format("Stc Idx {:X}", rw_static_reg->static_idx));
202 } else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) {
203 callbacks->CommandLog("Opcode: Debug Log");
204 callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
205 callbacks->CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id));
206 callbacks->CommandLog(
207 fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type)));
208 switch (debug_log->val_type) {
209 case DebugLogValueType::RegisterValue:
210 callbacks->CommandLog("Val Type: Register Value");
211 callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
212 break;
213 case DebugLogValueType::MemoryRelAddr:
214 callbacks->CommandLog("Val Type: Memory Relative Address");
215 callbacks->CommandLog(
216 fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
217 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
218 break;
219 case DebugLogValueType::MemoryOfsReg:
220 callbacks->CommandLog("Val Type: Memory Offset Register");
221 callbacks->CommandLog(
222 fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
223 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
224 break;
225 case DebugLogValueType::RegisterRelAddr:
226 callbacks->CommandLog("Val Type: Register Relative Address");
227 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
228 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
229 break;
230 case DebugLogValueType::RegisterOfsReg:
231 callbacks->CommandLog("Val Type: Register Offset Register");
232 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
233 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
234 break;
235 }
236 } else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) {
237 callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
238 }
239 }
240
241 DmntCheatVm::Callbacks::~Callbacks() = default;
242
DecodeNextOpcode(CheatVmOpcode & out)243 bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
244 // If we've ever seen a decode failure, return false.
245 bool valid = decode_success;
246 CheatVmOpcode opcode = {};
247 SCOPE_EXIT({
248 decode_success &= valid;
249 if (valid) {
250 out = opcode;
251 }
252 });
253
254 // Helper function for getting instruction dwords.
255 const auto GetNextDword = [&] {
256 if (instruction_ptr >= num_opcodes) {
257 valid = false;
258 return static_cast<u32>(0);
259 }
260 return program[instruction_ptr++];
261 };
262
263 // Helper function for parsing a VmInt.
264 const auto GetNextVmInt = [&](const u32 bit_width) {
265 VmInt val{};
266
267 const u32 first_dword = GetNextDword();
268 switch (bit_width) {
269 case 1:
270 val.bit8 = static_cast<u8>(first_dword);
271 break;
272 case 2:
273 val.bit16 = static_cast<u16>(first_dword);
274 break;
275 case 4:
276 val.bit32 = first_dword;
277 break;
278 case 8:
279 val.bit64 = (static_cast<u64>(first_dword) << 32ul) | static_cast<u64>(GetNextDword());
280 break;
281 }
282
283 return val;
284 };
285
286 // Read opcode.
287 const u32 first_dword = GetNextDword();
288 if (!valid) {
289 return valid;
290 }
291
292 auto opcode_type = static_cast<CheatVmOpcodeType>(((first_dword >> 28) & 0xF));
293 if (opcode_type >= CheatVmOpcodeType::ExtendedWidth) {
294 opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) |
295 ((first_dword >> 24) & 0xF));
296 }
297 if (opcode_type >= CheatVmOpcodeType::DoubleExtendedWidth) {
298 opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) |
299 ((first_dword >> 20) & 0xF));
300 }
301
302 // detect condition start.
303 switch (opcode_type) {
304 case CheatVmOpcodeType::BeginConditionalBlock:
305 case CheatVmOpcodeType::BeginKeypressConditionalBlock:
306 case CheatVmOpcodeType::BeginRegisterConditionalBlock:
307 opcode.begin_conditional_block = true;
308 break;
309 default:
310 opcode.begin_conditional_block = false;
311 break;
312 }
313
314 switch (opcode_type) {
315 case CheatVmOpcodeType::StoreStatic: {
316 // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
317 // Read additional words.
318 const u32 second_dword = GetNextDword();
319 const u32 bit_width = (first_dword >> 24) & 0xF;
320
321 opcode.opcode = StoreStaticOpcode{
322 .bit_width = bit_width,
323 .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
324 .offset_register = (first_dword >> 16) & 0xF,
325 .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
326 .value = GetNextVmInt(bit_width),
327 };
328 } break;
329 case CheatVmOpcodeType::BeginConditionalBlock: {
330 // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
331 // Read additional words.
332 const u32 second_dword = GetNextDword();
333 const u32 bit_width = (first_dword >> 24) & 0xF;
334
335 opcode.opcode = BeginConditionalOpcode{
336 .bit_width = bit_width,
337 .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
338 .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF),
339 .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
340 .value = GetNextVmInt(bit_width),
341 };
342 } break;
343 case CheatVmOpcodeType::EndConditionalBlock: {
344 // 20000000
345 // There's actually nothing left to process here!
346 opcode.opcode = EndConditionalOpcode{};
347 } break;
348 case CheatVmOpcodeType::ControlLoop: {
349 // 300R0000 VVVVVVVV
350 // 310R0000
351 // Parse register, whether loop start or loop end.
352 ControlLoopOpcode ctrl_loop{
353 .start_loop = ((first_dword >> 24) & 0xF) == 0,
354 .reg_index = (first_dword >> 20) & 0xF,
355 .num_iters = 0,
356 };
357
358 // Read number of iters if loop start.
359 if (ctrl_loop.start_loop) {
360 ctrl_loop.num_iters = GetNextDword();
361 }
362 opcode.opcode = ctrl_loop;
363 } break;
364 case CheatVmOpcodeType::LoadRegisterStatic: {
365 // 400R0000 VVVVVVVV VVVVVVVV
366 // Read additional words.
367 opcode.opcode = LoadRegisterStaticOpcode{
368 .reg_index = (first_dword >> 16) & 0xF,
369 .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(),
370 };
371 } break;
372 case CheatVmOpcodeType::LoadRegisterMemory: {
373 // 5TMRI0AA AAAAAAAA
374 // Read additional words.
375 const u32 second_dword = GetNextDword();
376 opcode.opcode = LoadRegisterMemoryOpcode{
377 .bit_width = (first_dword >> 24) & 0xF,
378 .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
379 .reg_index = ((first_dword >> 16) & 0xF),
380 .load_from_reg = ((first_dword >> 12) & 0xF) != 0,
381 .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
382 };
383 } break;
384 case CheatVmOpcodeType::StoreStaticToAddress: {
385 // 6T0RIor0 VVVVVVVV VVVVVVVV
386 // Read additional words.
387 opcode.opcode = StoreStaticToAddressOpcode{
388 .bit_width = (first_dword >> 24) & 0xF,
389 .reg_index = (first_dword >> 16) & 0xF,
390 .increment_reg = ((first_dword >> 12) & 0xF) != 0,
391 .add_offset_reg = ((first_dword >> 8) & 0xF) != 0,
392 .offset_reg_index = (first_dword >> 4) & 0xF,
393 .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(),
394 };
395 } break;
396 case CheatVmOpcodeType::PerformArithmeticStatic: {
397 // 7T0RC000 VVVVVVVV
398 // Read additional words.
399 opcode.opcode = PerformArithmeticStaticOpcode{
400 .bit_width = (first_dword >> 24) & 0xF,
401 .reg_index = ((first_dword >> 16) & 0xF),
402 .math_type = static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF),
403 .value = GetNextDword(),
404 };
405 } break;
406 case CheatVmOpcodeType::BeginKeypressConditionalBlock: {
407 // 8kkkkkkk
408 // Just parse the mask.
409 opcode.opcode = BeginKeypressConditionalOpcode{
410 .key_mask = first_dword & 0x0FFFFFFF,
411 };
412 } break;
413 case CheatVmOpcodeType::PerformArithmeticRegister: {
414 // 9TCRSIs0 (VVVVVVVV (VVVVVVVV))
415 PerformArithmeticRegisterOpcode perform_math_reg{
416 .bit_width = (first_dword >> 24) & 0xF,
417 .math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF),
418 .dst_reg_index = (first_dword >> 16) & 0xF,
419 .src_reg_1_index = (first_dword >> 12) & 0xF,
420 .src_reg_2_index = 0,
421 .has_immediate = ((first_dword >> 8) & 0xF) != 0,
422 .value = {},
423 };
424 if (perform_math_reg.has_immediate) {
425 perform_math_reg.src_reg_2_index = 0;
426 perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width);
427 } else {
428 perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
429 }
430 opcode.opcode = perform_math_reg;
431 } break;
432 case CheatVmOpcodeType::StoreRegisterToAddress: {
433 // ATSRIOxa (aaaaaaaa)
434 // A = opcode 10
435 // T = bit width
436 // S = src register index
437 // R = address register index
438 // I = 1 if increment address register, 0 if not increment address register
439 // O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region,
440 // 4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region +
441 // Relative Address
442 // x = offset register (for offset type 1), memory type (for offset type 3)
443 // a = relative address (for offset type 2+3)
444 StoreRegisterToAddressOpcode str_register{
445 .bit_width = (first_dword >> 24) & 0xF,
446 .str_reg_index = (first_dword >> 20) & 0xF,
447 .addr_reg_index = (first_dword >> 16) & 0xF,
448 .increment_reg = ((first_dword >> 12) & 0xF) != 0,
449 .ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)),
450 .mem_type = MemoryAccessType::MainNso,
451 .ofs_reg_index = (first_dword >> 4) & 0xF,
452 .rel_address = 0,
453 };
454 switch (str_register.ofs_type) {
455 case StoreRegisterOffsetType::None:
456 case StoreRegisterOffsetType::Reg:
457 // Nothing more to do
458 break;
459 case StoreRegisterOffsetType::Imm:
460 str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
461 break;
462 case StoreRegisterOffsetType::MemReg:
463 str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
464 break;
465 case StoreRegisterOffsetType::MemImm:
466 case StoreRegisterOffsetType::MemImmReg:
467 str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
468 str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
469 break;
470 default:
471 str_register.ofs_type = StoreRegisterOffsetType::None;
472 break;
473 }
474 opcode.opcode = str_register;
475 } break;
476 case CheatVmOpcodeType::BeginRegisterConditionalBlock: {
477 // C0TcSX##
478 // C0TcS0Ma aaaaaaaa
479 // C0TcS1Mr
480 // C0TcS2Ra aaaaaaaa
481 // C0TcS3Rr
482 // C0TcS400 VVVVVVVV (VVVVVVVV)
483 // C0TcS5X0
484 // C0 = opcode 0xC0
485 // T = bit width
486 // c = condition type.
487 // S = source register.
488 // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
489 // register,
490 // 2 = register with relative offset, 3 = register with offset register, 4 = static
491 // value, 5 = other register.
492 // M = memory type.
493 // R = address register.
494 // a = relative address.
495 // r = offset register.
496 // X = other register.
497 // V = value.
498
499 BeginRegisterConditionalOpcode begin_reg_cond{
500 .bit_width = (first_dword >> 20) & 0xF,
501 .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF),
502 .val_reg_index = (first_dword >> 12) & 0xF,
503 .comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF),
504 .mem_type = MemoryAccessType::MainNso,
505 .addr_reg_index = 0,
506 .other_reg_index = 0,
507 .ofs_reg_index = 0,
508 .rel_address = 0,
509 .value = {},
510 };
511
512 switch (begin_reg_cond.comp_type) {
513 case CompareRegisterValueType::StaticValue:
514 begin_reg_cond.value = GetNextVmInt(begin_reg_cond.bit_width);
515 break;
516 case CompareRegisterValueType::OtherRegister:
517 begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
518 break;
519 case CompareRegisterValueType::MemoryRelAddr:
520 begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
521 begin_reg_cond.rel_address =
522 (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
523 break;
524 case CompareRegisterValueType::MemoryOfsReg:
525 begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
526 begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
527 break;
528 case CompareRegisterValueType::RegisterRelAddr:
529 begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF;
530 begin_reg_cond.rel_address =
531 (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
532 break;
533 case CompareRegisterValueType::RegisterOfsReg:
534 begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF;
535 begin_reg_cond.ofs_reg_index = first_dword & 0xF;
536 break;
537 }
538 opcode.opcode = begin_reg_cond;
539 } break;
540 case CheatVmOpcodeType::SaveRestoreRegister: {
541 // C10D0Sx0
542 // C1 = opcode 0xC1
543 // D = destination index.
544 // S = source index.
545 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
546 // a register.
547 // NOTE: If we add more save slots later, current encoding is backwards compatible.
548 opcode.opcode = SaveRestoreRegisterOpcode{
549 .dst_index = (first_dword >> 16) & 0xF,
550 .src_index = (first_dword >> 8) & 0xF,
551 .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF),
552 };
553 } break;
554 case CheatVmOpcodeType::SaveRestoreRegisterMask: {
555 // C2x0XXXX
556 // C2 = opcode 0xC2
557 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring.
558 // X = 16-bit bitmask, bit i --> save or restore register i.
559 SaveRestoreRegisterMaskOpcode save_restore_regmask{
560 .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF),
561 .should_operate = {},
562 };
563 for (std::size_t i = 0; i < NumRegisters; i++) {
564 save_restore_regmask.should_operate[i] = (first_dword & (1U << i)) != 0;
565 }
566 opcode.opcode = save_restore_regmask;
567 } break;
568 case CheatVmOpcodeType::ReadWriteStaticRegister: {
569 // C3000XXx
570 // C3 = opcode 0xC3.
571 // XX = static register index.
572 // x = register index.
573 opcode.opcode = ReadWriteStaticRegisterOpcode{
574 .static_idx = (first_dword >> 4) & 0xFF,
575 .idx = first_dword & 0xF,
576 };
577 } break;
578 case CheatVmOpcodeType::DebugLog: {
579 // FFFTIX##
580 // FFFTI0Ma aaaaaaaa
581 // FFFTI1Mr
582 // FFFTI2Ra aaaaaaaa
583 // FFFTI3Rr
584 // FFFTI4X0
585 // FFF = opcode 0xFFF
586 // T = bit width.
587 // I = log id.
588 // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
589 // register,
590 // 2 = register with relative offset, 3 = register with offset register, 4 = register
591 // value.
592 // M = memory type.
593 // R = address register.
594 // a = relative address.
595 // r = offset register.
596 // X = value register.
597 DebugLogOpcode debug_log{
598 .bit_width = (first_dword >> 16) & 0xF,
599 .log_id = (first_dword >> 12) & 0xF,
600 .val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF),
601 .mem_type = MemoryAccessType::MainNso,
602 .addr_reg_index = 0,
603 .val_reg_index = 0,
604 .ofs_reg_index = 0,
605 .rel_address = 0,
606 };
607
608 switch (debug_log.val_type) {
609 case DebugLogValueType::RegisterValue:
610 debug_log.val_reg_index = (first_dword >> 4) & 0xF;
611 break;
612 case DebugLogValueType::MemoryRelAddr:
613 debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
614 debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
615 break;
616 case DebugLogValueType::MemoryOfsReg:
617 debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
618 debug_log.ofs_reg_index = first_dword & 0xF;
619 break;
620 case DebugLogValueType::RegisterRelAddr:
621 debug_log.addr_reg_index = (first_dword >> 4) & 0xF;
622 debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
623 break;
624 case DebugLogValueType::RegisterOfsReg:
625 debug_log.addr_reg_index = (first_dword >> 4) & 0xF;
626 debug_log.ofs_reg_index = first_dword & 0xF;
627 break;
628 }
629 opcode.opcode = debug_log;
630 } break;
631 case CheatVmOpcodeType::ExtendedWidth:
632 case CheatVmOpcodeType::DoubleExtendedWidth:
633 default:
634 // Unrecognized instruction cannot be decoded.
635 valid = false;
636 opcode.opcode = UnrecognizedInstruction{opcode_type};
637 break;
638 }
639
640 // End decoding.
641 return valid;
642 }
643
SkipConditionalBlock()644 void DmntCheatVm::SkipConditionalBlock() {
645 if (condition_depth > 0) {
646 // We want to continue until we're out of the current block.
647 const std::size_t desired_depth = condition_depth - 1;
648
649 CheatVmOpcode skip_opcode{};
650 while (condition_depth > desired_depth && DecodeNextOpcode(skip_opcode)) {
651 // Decode instructions until we see end of the current conditional block.
652 // NOTE: This is broken in gateway's implementation.
653 // Gateway currently checks for "0x2" instead of "0x20000000"
654 // In addition, they do a linear scan instead of correctly decoding opcodes.
655 // This causes issues if "0x2" appears as an immediate in the conditional block...
656
657 // We also support nesting of conditional blocks, and Gateway does not.
658 if (skip_opcode.begin_conditional_block) {
659 condition_depth++;
660 } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
661 condition_depth--;
662 }
663 }
664 } else {
665 // Skipping, but condition_depth = 0.
666 // This is an error condition.
667 // However, I don't actually believe it is possible for this to happen.
668 // I guess we'll throw a fatal error here, so as to encourage me to fix the VM
669 // in the event that someone triggers it? I don't know how you'd do that.
670 UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM");
671 }
672 }
673
GetVmInt(VmInt value,u32 bit_width)674 u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
675 switch (bit_width) {
676 case 1:
677 return value.bit8;
678 case 2:
679 return value.bit16;
680 case 4:
681 return value.bit32;
682 case 8:
683 return value.bit64;
684 default:
685 // Invalid bit width -> return 0.
686 return 0;
687 }
688 }
689
GetCheatProcessAddress(const CheatProcessMetadata & metadata,MemoryAccessType mem_type,u64 rel_address)690 u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
691 MemoryAccessType mem_type, u64 rel_address) {
692 switch (mem_type) {
693 case MemoryAccessType::MainNso:
694 default:
695 return metadata.main_nso_extents.base + rel_address;
696 case MemoryAccessType::Heap:
697 return metadata.heap_extents.base + rel_address;
698 }
699 }
700
ResetState()701 void DmntCheatVm::ResetState() {
702 registers.fill(0);
703 saved_values.fill(0);
704 loop_tops.fill(0);
705 static_registers.fill(0);
706 instruction_ptr = 0;
707 condition_depth = 0;
708 decode_success = true;
709 }
710
LoadProgram(const std::vector<CheatEntry> & entries)711 bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
712 // Reset opcode count.
713 num_opcodes = 0;
714
715 for (std::size_t i = 0; i < entries.size(); i++) {
716 if (entries[i].enabled) {
717 // Bounds check.
718 if (entries[i].definition.num_opcodes + num_opcodes > MaximumProgramOpcodeCount) {
719 num_opcodes = 0;
720 return false;
721 }
722
723 for (std::size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
724 program[num_opcodes++] = entries[i].definition.opcodes[n];
725 }
726 }
727 }
728
729 return true;
730 }
731
Execute(const CheatProcessMetadata & metadata)732 void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
733 CheatVmOpcode cur_opcode{};
734
735 // Get Keys down.
736 u64 kDown = callbacks->HidKeysDown();
737
738 callbacks->CommandLog("Started VM execution.");
739 callbacks->CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base));
740 callbacks->CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base));
741 callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF)));
742
743 // Clear VM state.
744 ResetState();
745
746 // Loop until program finishes.
747 while (DecodeNextOpcode(cur_opcode)) {
748 callbacks->CommandLog(
749 fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr)));
750
751 for (std::size_t i = 0; i < NumRegisters; i++) {
752 callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
753 }
754
755 for (std::size_t i = 0; i < NumRegisters; i++) {
756 callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
757 }
758 LogOpcode(cur_opcode);
759
760 // Increment conditional depth, if relevant.
761 if (cur_opcode.begin_conditional_block) {
762 condition_depth++;
763 }
764
765 if (auto store_static = std::get_if<StoreStaticOpcode>(&cur_opcode.opcode)) {
766 // Calculate address, write value to memory.
767 u64 dst_address = GetCheatProcessAddress(metadata, store_static->mem_type,
768 store_static->rel_address +
769 registers[store_static->offset_register]);
770 u64 dst_value = GetVmInt(store_static->value, store_static->bit_width);
771 switch (store_static->bit_width) {
772 case 1:
773 case 2:
774 case 4:
775 case 8:
776 callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width);
777 break;
778 }
779 } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
780 // Read value from memory.
781 u64 src_address =
782 GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address);
783 u64 src_value = 0;
784 switch (store_static->bit_width) {
785 case 1:
786 case 2:
787 case 4:
788 case 8:
789 callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width);
790 break;
791 }
792 // Check against condition.
793 u64 cond_value = GetVmInt(begin_cond->value, begin_cond->bit_width);
794 bool cond_met = false;
795 switch (begin_cond->cond_type) {
796 case ConditionalComparisonType::GT:
797 cond_met = src_value > cond_value;
798 break;
799 case ConditionalComparisonType::GE:
800 cond_met = src_value >= cond_value;
801 break;
802 case ConditionalComparisonType::LT:
803 cond_met = src_value < cond_value;
804 break;
805 case ConditionalComparisonType::LE:
806 cond_met = src_value <= cond_value;
807 break;
808 case ConditionalComparisonType::EQ:
809 cond_met = src_value == cond_value;
810 break;
811 case ConditionalComparisonType::NE:
812 cond_met = src_value != cond_value;
813 break;
814 }
815 // Skip conditional block if condition not met.
816 if (!cond_met) {
817 SkipConditionalBlock();
818 }
819 } else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) {
820 // Decrement the condition depth.
821 // We will assume, graciously, that mismatched conditional block ends are a nop.
822 if (condition_depth > 0) {
823 condition_depth--;
824 }
825 } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
826 if (ctrl_loop->start_loop) {
827 // Start a loop.
828 registers[ctrl_loop->reg_index] = ctrl_loop->num_iters;
829 loop_tops[ctrl_loop->reg_index] = instruction_ptr;
830 } else {
831 // End a loop.
832 registers[ctrl_loop->reg_index]--;
833 if (registers[ctrl_loop->reg_index] != 0) {
834 instruction_ptr = loop_tops[ctrl_loop->reg_index];
835 }
836 }
837 } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&cur_opcode.opcode)) {
838 // Set a register to a static value.
839 registers[ldr_static->reg_index] = ldr_static->value;
840 } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&cur_opcode.opcode)) {
841 // Choose source address.
842 u64 src_address;
843 if (ldr_memory->load_from_reg) {
844 src_address = registers[ldr_memory->reg_index] + ldr_memory->rel_address;
845 } else {
846 src_address =
847 GetCheatProcessAddress(metadata, ldr_memory->mem_type, ldr_memory->rel_address);
848 }
849 // Read into register. Gateway only reads on valid bitwidth.
850 switch (ldr_memory->bit_width) {
851 case 1:
852 case 2:
853 case 4:
854 case 8:
855 callbacks->MemoryRead(src_address, ®isters[ldr_memory->reg_index],
856 ldr_memory->bit_width);
857 break;
858 }
859 } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
860 // Calculate address.
861 u64 dst_address = registers[str_static->reg_index];
862 u64 dst_value = str_static->value;
863 if (str_static->add_offset_reg) {
864 dst_address += registers[str_static->offset_reg_index];
865 }
866 // Write value to memory. Gateway only writes on valid bitwidth.
867 switch (str_static->bit_width) {
868 case 1:
869 case 2:
870 case 4:
871 case 8:
872 callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width);
873 break;
874 }
875 // Increment register if relevant.
876 if (str_static->increment_reg) {
877 registers[str_static->reg_index] += str_static->bit_width;
878 }
879 } else if (auto perform_math_static =
880 std::get_if<PerformArithmeticStaticOpcode>(&cur_opcode.opcode)) {
881 // Do requested math.
882 switch (perform_math_static->math_type) {
883 case RegisterArithmeticType::Addition:
884 registers[perform_math_static->reg_index] +=
885 static_cast<u64>(perform_math_static->value);
886 break;
887 case RegisterArithmeticType::Subtraction:
888 registers[perform_math_static->reg_index] -=
889 static_cast<u64>(perform_math_static->value);
890 break;
891 case RegisterArithmeticType::Multiplication:
892 registers[perform_math_static->reg_index] *=
893 static_cast<u64>(perform_math_static->value);
894 break;
895 case RegisterArithmeticType::LeftShift:
896 registers[perform_math_static->reg_index] <<=
897 static_cast<u64>(perform_math_static->value);
898 break;
899 case RegisterArithmeticType::RightShift:
900 registers[perform_math_static->reg_index] >>=
901 static_cast<u64>(perform_math_static->value);
902 break;
903 default:
904 // Do not handle extensions here.
905 break;
906 }
907 // Apply bit width.
908 switch (perform_math_static->bit_width) {
909 case 1:
910 registers[perform_math_static->reg_index] =
911 static_cast<u8>(registers[perform_math_static->reg_index]);
912 break;
913 case 2:
914 registers[perform_math_static->reg_index] =
915 static_cast<u16>(registers[perform_math_static->reg_index]);
916 break;
917 case 4:
918 registers[perform_math_static->reg_index] =
919 static_cast<u32>(registers[perform_math_static->reg_index]);
920 break;
921 case 8:
922 registers[perform_math_static->reg_index] =
923 static_cast<u64>(registers[perform_math_static->reg_index]);
924 break;
925 }
926 } else if (auto begin_keypress_cond =
927 std::get_if<BeginKeypressConditionalOpcode>(&cur_opcode.opcode)) {
928 // Check for keypress.
929 if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
930 // Keys not pressed. Skip conditional block.
931 SkipConditionalBlock();
932 }
933 } else if (auto perform_math_reg =
934 std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
935 const u64 operand_1_value = registers[perform_math_reg->src_reg_1_index];
936 const u64 operand_2_value =
937 perform_math_reg->has_immediate
938 ? GetVmInt(perform_math_reg->value, perform_math_reg->bit_width)
939 : registers[perform_math_reg->src_reg_2_index];
940
941 u64 res_val = 0;
942 // Do requested math.
943 switch (perform_math_reg->math_type) {
944 case RegisterArithmeticType::Addition:
945 res_val = operand_1_value + operand_2_value;
946 break;
947 case RegisterArithmeticType::Subtraction:
948 res_val = operand_1_value - operand_2_value;
949 break;
950 case RegisterArithmeticType::Multiplication:
951 res_val = operand_1_value * operand_2_value;
952 break;
953 case RegisterArithmeticType::LeftShift:
954 res_val = operand_1_value << operand_2_value;
955 break;
956 case RegisterArithmeticType::RightShift:
957 res_val = operand_1_value >> operand_2_value;
958 break;
959 case RegisterArithmeticType::LogicalAnd:
960 res_val = operand_1_value & operand_2_value;
961 break;
962 case RegisterArithmeticType::LogicalOr:
963 res_val = operand_1_value | operand_2_value;
964 break;
965 case RegisterArithmeticType::LogicalNot:
966 res_val = ~operand_1_value;
967 break;
968 case RegisterArithmeticType::LogicalXor:
969 res_val = operand_1_value ^ operand_2_value;
970 break;
971 case RegisterArithmeticType::None:
972 res_val = operand_1_value;
973 break;
974 }
975
976 // Apply bit width.
977 switch (perform_math_reg->bit_width) {
978 case 1:
979 res_val = static_cast<u8>(res_val);
980 break;
981 case 2:
982 res_val = static_cast<u16>(res_val);
983 break;
984 case 4:
985 res_val = static_cast<u32>(res_val);
986 break;
987 case 8:
988 res_val = static_cast<u64>(res_val);
989 break;
990 }
991
992 // Save to register.
993 registers[perform_math_reg->dst_reg_index] = res_val;
994 } else if (auto str_register =
995 std::get_if<StoreRegisterToAddressOpcode>(&cur_opcode.opcode)) {
996 // Calculate address.
997 u64 dst_value = registers[str_register->str_reg_index];
998 u64 dst_address = registers[str_register->addr_reg_index];
999 switch (str_register->ofs_type) {
1000 case StoreRegisterOffsetType::None:
1001 // Nothing more to do
1002 break;
1003 case StoreRegisterOffsetType::Reg:
1004 dst_address += registers[str_register->ofs_reg_index];
1005 break;
1006 case StoreRegisterOffsetType::Imm:
1007 dst_address += str_register->rel_address;
1008 break;
1009 case StoreRegisterOffsetType::MemReg:
1010 dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
1011 registers[str_register->addr_reg_index]);
1012 break;
1013 case StoreRegisterOffsetType::MemImm:
1014 dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
1015 str_register->rel_address);
1016 break;
1017 case StoreRegisterOffsetType::MemImmReg:
1018 dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
1019 registers[str_register->addr_reg_index] +
1020 str_register->rel_address);
1021 break;
1022 }
1023
1024 // Write value to memory. Write only on valid bitwidth.
1025 switch (str_register->bit_width) {
1026 case 1:
1027 case 2:
1028 case 4:
1029 case 8:
1030 callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width);
1031 break;
1032 }
1033
1034 // Increment register if relevant.
1035 if (str_register->increment_reg) {
1036 registers[str_register->addr_reg_index] += str_register->bit_width;
1037 }
1038 } else if (auto begin_reg_cond =
1039 std::get_if<BeginRegisterConditionalOpcode>(&cur_opcode.opcode)) {
1040 // Get value from register.
1041 u64 src_value = 0;
1042 switch (begin_reg_cond->bit_width) {
1043 case 1:
1044 src_value = static_cast<u8>(registers[begin_reg_cond->val_reg_index] & 0xFFul);
1045 break;
1046 case 2:
1047 src_value = static_cast<u16>(registers[begin_reg_cond->val_reg_index] & 0xFFFFul);
1048 break;
1049 case 4:
1050 src_value =
1051 static_cast<u32>(registers[begin_reg_cond->val_reg_index] & 0xFFFFFFFFul);
1052 break;
1053 case 8:
1054 src_value = static_cast<u64>(registers[begin_reg_cond->val_reg_index] &
1055 0xFFFFFFFFFFFFFFFFul);
1056 break;
1057 }
1058
1059 // Read value from memory.
1060 u64 cond_value = 0;
1061 if (begin_reg_cond->comp_type == CompareRegisterValueType::StaticValue) {
1062 cond_value = GetVmInt(begin_reg_cond->value, begin_reg_cond->bit_width);
1063 } else if (begin_reg_cond->comp_type == CompareRegisterValueType::OtherRegister) {
1064 switch (begin_reg_cond->bit_width) {
1065 case 1:
1066 cond_value =
1067 static_cast<u8>(registers[begin_reg_cond->other_reg_index] & 0xFFul);
1068 break;
1069 case 2:
1070 cond_value =
1071 static_cast<u16>(registers[begin_reg_cond->other_reg_index] & 0xFFFFul);
1072 break;
1073 case 4:
1074 cond_value =
1075 static_cast<u32>(registers[begin_reg_cond->other_reg_index] & 0xFFFFFFFFul);
1076 break;
1077 case 8:
1078 cond_value = static_cast<u64>(registers[begin_reg_cond->other_reg_index] &
1079 0xFFFFFFFFFFFFFFFFul);
1080 break;
1081 }
1082 } else {
1083 u64 cond_address = 0;
1084 switch (begin_reg_cond->comp_type) {
1085 case CompareRegisterValueType::MemoryRelAddr:
1086 cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type,
1087 begin_reg_cond->rel_address);
1088 break;
1089 case CompareRegisterValueType::MemoryOfsReg:
1090 cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type,
1091 registers[begin_reg_cond->ofs_reg_index]);
1092 break;
1093 case CompareRegisterValueType::RegisterRelAddr:
1094 cond_address =
1095 registers[begin_reg_cond->addr_reg_index] + begin_reg_cond->rel_address;
1096 break;
1097 case CompareRegisterValueType::RegisterOfsReg:
1098 cond_address = registers[begin_reg_cond->addr_reg_index] +
1099 registers[begin_reg_cond->ofs_reg_index];
1100 break;
1101 default:
1102 break;
1103 }
1104 switch (begin_reg_cond->bit_width) {
1105 case 1:
1106 case 2:
1107 case 4:
1108 case 8:
1109 callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width);
1110 break;
1111 }
1112 }
1113
1114 // Check against condition.
1115 bool cond_met = false;
1116 switch (begin_reg_cond->cond_type) {
1117 case ConditionalComparisonType::GT:
1118 cond_met = src_value > cond_value;
1119 break;
1120 case ConditionalComparisonType::GE:
1121 cond_met = src_value >= cond_value;
1122 break;
1123 case ConditionalComparisonType::LT:
1124 cond_met = src_value < cond_value;
1125 break;
1126 case ConditionalComparisonType::LE:
1127 cond_met = src_value <= cond_value;
1128 break;
1129 case ConditionalComparisonType::EQ:
1130 cond_met = src_value == cond_value;
1131 break;
1132 case ConditionalComparisonType::NE:
1133 cond_met = src_value != cond_value;
1134 break;
1135 }
1136
1137 // Skip conditional block if condition not met.
1138 if (!cond_met) {
1139 SkipConditionalBlock();
1140 }
1141 } else if (auto save_restore_reg =
1142 std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
1143 // Save or restore a register.
1144 switch (save_restore_reg->op_type) {
1145 case SaveRestoreRegisterOpType::ClearRegs:
1146 registers[save_restore_reg->dst_index] = 0ul;
1147 break;
1148 case SaveRestoreRegisterOpType::ClearSaved:
1149 saved_values[save_restore_reg->dst_index] = 0ul;
1150 break;
1151 case SaveRestoreRegisterOpType::Save:
1152 saved_values[save_restore_reg->dst_index] = registers[save_restore_reg->src_index];
1153 break;
1154 case SaveRestoreRegisterOpType::Restore:
1155 default:
1156 registers[save_restore_reg->dst_index] = saved_values[save_restore_reg->src_index];
1157 break;
1158 }
1159 } else if (auto save_restore_regmask =
1160 std::get_if<SaveRestoreRegisterMaskOpcode>(&cur_opcode.opcode)) {
1161 // Save or restore register mask.
1162 u64* src;
1163 u64* dst;
1164 switch (save_restore_regmask->op_type) {
1165 case SaveRestoreRegisterOpType::ClearSaved:
1166 case SaveRestoreRegisterOpType::Save:
1167 src = registers.data();
1168 dst = saved_values.data();
1169 break;
1170 case SaveRestoreRegisterOpType::ClearRegs:
1171 case SaveRestoreRegisterOpType::Restore:
1172 default:
1173 src = saved_values.data();
1174 dst = registers.data();
1175 break;
1176 }
1177 for (std::size_t i = 0; i < NumRegisters; i++) {
1178 if (save_restore_regmask->should_operate[i]) {
1179 switch (save_restore_regmask->op_type) {
1180 case SaveRestoreRegisterOpType::ClearSaved:
1181 case SaveRestoreRegisterOpType::ClearRegs:
1182 dst[i] = 0ul;
1183 break;
1184 case SaveRestoreRegisterOpType::Save:
1185 case SaveRestoreRegisterOpType::Restore:
1186 default:
1187 dst[i] = src[i];
1188 break;
1189 }
1190 }
1191 }
1192 } else if (auto rw_static_reg =
1193 std::get_if<ReadWriteStaticRegisterOpcode>(&cur_opcode.opcode)) {
1194 if (rw_static_reg->static_idx < NumReadableStaticRegisters) {
1195 // Load a register with a static register.
1196 registers[rw_static_reg->idx] = static_registers[rw_static_reg->static_idx];
1197 } else {
1198 // Store a register to a static register.
1199 static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
1200 }
1201 } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
1202 // Read value from memory.
1203 u64 log_value = 0;
1204 if (debug_log->val_type == DebugLogValueType::RegisterValue) {
1205 switch (debug_log->bit_width) {
1206 case 1:
1207 log_value = static_cast<u8>(registers[debug_log->val_reg_index] & 0xFFul);
1208 break;
1209 case 2:
1210 log_value = static_cast<u16>(registers[debug_log->val_reg_index] & 0xFFFFul);
1211 break;
1212 case 4:
1213 log_value =
1214 static_cast<u32>(registers[debug_log->val_reg_index] & 0xFFFFFFFFul);
1215 break;
1216 case 8:
1217 log_value = static_cast<u64>(registers[debug_log->val_reg_index] &
1218 0xFFFFFFFFFFFFFFFFul);
1219 break;
1220 }
1221 } else {
1222 u64 val_address = 0;
1223 switch (debug_log->val_type) {
1224 case DebugLogValueType::MemoryRelAddr:
1225 val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
1226 debug_log->rel_address);
1227 break;
1228 case DebugLogValueType::MemoryOfsReg:
1229 val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
1230 registers[debug_log->ofs_reg_index]);
1231 break;
1232 case DebugLogValueType::RegisterRelAddr:
1233 val_address = registers[debug_log->addr_reg_index] + debug_log->rel_address;
1234 break;
1235 case DebugLogValueType::RegisterOfsReg:
1236 val_address =
1237 registers[debug_log->addr_reg_index] + registers[debug_log->ofs_reg_index];
1238 break;
1239 default:
1240 break;
1241 }
1242 switch (debug_log->bit_width) {
1243 case 1:
1244 case 2:
1245 case 4:
1246 case 8:
1247 callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width);
1248 break;
1249 }
1250 }
1251
1252 // Log value.
1253 DebugLog(debug_log->log_id, log_value);
1254 }
1255 }
1256 }
1257
1258 } // namespace Core::Memory
1259