dmnt_cheat_vm.cpp 56 KB

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