dmnt_cheat_vm.cpp 56 KB

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