dmnt_cheat_vm.cpp 55 KB

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