| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206 |
- /*
- * Copyright (c) 2018-2019 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /*
- * Adapted by DarkLordZach for use/interaction with yuzu
- *
- * Modifications Copyright 2019 yuzu emulator team
- * Licensed under GPLv2 or any later version
- * Refer to the license.txt file included.
- */
- #include "common/assert.h"
- #include "common/scope_exit.h"
- #include "core/memory/dmnt_cheat_types.h"
- #include "core/memory/dmnt_cheat_vm.h"
- namespace Memory {
- void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
- callbacks->DebugLog(static_cast<u8>(log_id), value);
- }
- void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
- switch (opcode.opcode) {
- case CheatVmOpcodeType_StoreStatic:
- this->LogToDebugFile("Opcode: Store Static\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.store_static.bit_width);
- this->LogToDebugFile("Mem Type: %x\n", opcode.store_static.mem_type);
- this->LogToDebugFile("Reg Idx: %x\n", opcode.store_static.offset_register);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.store_static.rel_address);
- this->LogToDebugFile("Value: %lx\n", opcode.store_static.value.bit64);
- break;
- case CheatVmOpcodeType_BeginConditionalBlock:
- this->LogToDebugFile("Opcode: Begin Conditional\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.begin_cond.bit_width);
- this->LogToDebugFile("Mem Type: %x\n", opcode.begin_cond.mem_type);
- this->LogToDebugFile("Cond Type: %x\n", opcode.begin_cond.cond_type);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_cond.rel_address);
- this->LogToDebugFile("Value: %lx\n", opcode.begin_cond.value.bit64);
- break;
- case CheatVmOpcodeType_EndConditionalBlock:
- this->LogToDebugFile("Opcode: End Conditional\n");
- break;
- case CheatVmOpcodeType_ControlLoop:
- if (opcode.ctrl_loop.start_loop) {
- this->LogToDebugFile("Opcode: Start Loop\n");
- this->LogToDebugFile("Reg Idx: %x\n", opcode.ctrl_loop.reg_index);
- this->LogToDebugFile("Num Iters: %x\n", opcode.ctrl_loop.num_iters);
- } else {
- this->LogToDebugFile("Opcode: End Loop\n");
- this->LogToDebugFile("Reg Idx: %x\n", opcode.ctrl_loop.reg_index);
- }
- break;
- case CheatVmOpcodeType_LoadRegisterStatic:
- this->LogToDebugFile("Opcode: Load Register Static\n");
- this->LogToDebugFile("Reg Idx: %x\n", opcode.ldr_static.reg_index);
- this->LogToDebugFile("Value: %lx\n", opcode.ldr_static.value);
- break;
- case CheatVmOpcodeType_LoadRegisterMemory:
- this->LogToDebugFile("Opcode: Load Register Memory\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.ldr_memory.bit_width);
- this->LogToDebugFile("Reg Idx: %x\n", opcode.ldr_memory.reg_index);
- this->LogToDebugFile("Mem Type: %x\n", opcode.ldr_memory.mem_type);
- this->LogToDebugFile("From Reg: %d\n", opcode.ldr_memory.load_from_reg);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.ldr_memory.rel_address);
- break;
- case CheatVmOpcodeType_StoreStaticToAddress:
- this->LogToDebugFile("Opcode: Store Static to Address\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.str_static.bit_width);
- this->LogToDebugFile("Reg Idx: %x\n", opcode.str_static.reg_index);
- if (opcode.str_static.add_offset_reg) {
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_static.offset_reg_index);
- }
- this->LogToDebugFile("Incr Reg: %d\n", opcode.str_static.increment_reg);
- this->LogToDebugFile("Value: %lx\n", opcode.str_static.value);
- break;
- case CheatVmOpcodeType_PerformArithmeticStatic:
- this->LogToDebugFile("Opcode: Perform Static Arithmetic\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_static.bit_width);
- this->LogToDebugFile("Reg Idx: %x\n", opcode.perform_math_static.reg_index);
- this->LogToDebugFile("Math Type: %x\n", opcode.perform_math_static.math_type);
- this->LogToDebugFile("Value: %lx\n", opcode.perform_math_static.value);
- break;
- case CheatVmOpcodeType_BeginKeypressConditionalBlock:
- this->LogToDebugFile("Opcode: Begin Keypress Conditional\n");
- this->LogToDebugFile("Key Mask: %x\n", opcode.begin_keypress_cond.key_mask);
- break;
- case CheatVmOpcodeType_PerformArithmeticRegister:
- this->LogToDebugFile("Opcode: Perform Register Arithmetic\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_reg.bit_width);
- this->LogToDebugFile("Dst Idx: %x\n", opcode.perform_math_reg.dst_reg_index);
- this->LogToDebugFile("Src1 Idx: %x\n", opcode.perform_math_reg.src_reg_1_index);
- if (opcode.perform_math_reg.has_immediate) {
- this->LogToDebugFile("Value: %lx\n", opcode.perform_math_reg.value.bit64);
- } else {
- this->LogToDebugFile("Src2 Idx: %x\n", opcode.perform_math_reg.src_reg_2_index);
- }
- break;
- case CheatVmOpcodeType_StoreRegisterToAddress:
- this->LogToDebugFile("Opcode: Store Register to Address\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.str_register.bit_width);
- this->LogToDebugFile("S Reg Idx: %x\n", opcode.str_register.str_reg_index);
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.str_register.addr_reg_index);
- this->LogToDebugFile("Incr Reg: %d\n", opcode.str_register.increment_reg);
- switch (opcode.str_register.ofs_type) {
- case StoreRegisterOffsetType_None:
- break;
- case StoreRegisterOffsetType_Reg:
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_register.ofs_reg_index);
- break;
- case StoreRegisterOffsetType_Imm:
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.str_register.rel_address);
- break;
- case StoreRegisterOffsetType_MemReg:
- this->LogToDebugFile("Mem Type: %x\n", opcode.str_register.mem_type);
- break;
- case StoreRegisterOffsetType_MemImm:
- case StoreRegisterOffsetType_MemImmReg:
- this->LogToDebugFile("Mem Type: %x\n", opcode.str_register.mem_type);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.str_register.rel_address);
- break;
- }
- break;
- case CheatVmOpcodeType_BeginRegisterConditionalBlock:
- this->LogToDebugFile("Opcode: Begin Register Conditional\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.begin_reg_cond.bit_width);
- this->LogToDebugFile("Cond Type: %x\n", opcode.begin_reg_cond.cond_type);
- this->LogToDebugFile("V Reg Idx: %x\n", opcode.begin_reg_cond.val_reg_index);
- switch (opcode.begin_reg_cond.comp_type) {
- case CompareRegisterValueType_StaticValue:
- this->LogToDebugFile("Comp Type: Static Value\n");
- this->LogToDebugFile("Value: %lx\n", opcode.begin_reg_cond.value.bit64);
- break;
- case CompareRegisterValueType_OtherRegister:
- this->LogToDebugFile("Comp Type: Other Register\n");
- this->LogToDebugFile("X Reg Idx: %x\n", opcode.begin_reg_cond.other_reg_index);
- break;
- case CompareRegisterValueType_MemoryRelAddr:
- this->LogToDebugFile("Comp Type: Memory Relative Address\n");
- this->LogToDebugFile("Mem Type: %x\n", opcode.begin_reg_cond.mem_type);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_reg_cond.rel_address);
- break;
- case CompareRegisterValueType_MemoryOfsReg:
- this->LogToDebugFile("Comp Type: Memory Offset Register\n");
- this->LogToDebugFile("Mem Type: %x\n", opcode.begin_reg_cond.mem_type);
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index);
- break;
- case CompareRegisterValueType_RegisterRelAddr:
- this->LogToDebugFile("Comp Type: Register Relative Address\n");
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_reg_cond.rel_address);
- break;
- case CompareRegisterValueType_RegisterOfsReg:
- this->LogToDebugFile("Comp Type: Register Offset Register\n");
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index);
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index);
- break;
- }
- break;
- case CheatVmOpcodeType_SaveRestoreRegister:
- this->LogToDebugFile("Opcode: Save or Restore Register\n");
- this->LogToDebugFile("Dst Idx: %x\n", opcode.save_restore_reg.dst_index);
- this->LogToDebugFile("Src Idx: %x\n", opcode.save_restore_reg.src_index);
- this->LogToDebugFile("Op Type: %d\n", opcode.save_restore_reg.op_type);
- break;
- case CheatVmOpcodeType_SaveRestoreRegisterMask:
- this->LogToDebugFile("Opcode: Save or Restore Register Mask\n");
- this->LogToDebugFile("Op Type: %d\n", opcode.save_restore_regmask.op_type);
- for (size_t i = 0; i < NumRegisters; i++) {
- this->LogToDebugFile("Act[%02x]: %d\n", i,
- opcode.save_restore_regmask.should_operate[i]);
- }
- break;
- case CheatVmOpcodeType_DebugLog:
- this->LogToDebugFile("Opcode: Debug Log\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.debug_log.bit_width);
- this->LogToDebugFile("Log ID: %x\n", opcode.debug_log.log_id);
- this->LogToDebugFile("Val Type: %x\n", opcode.debug_log.val_type);
- switch (opcode.debug_log.val_type) {
- case DebugLogValueType_RegisterValue:
- this->LogToDebugFile("Val Type: Register Value\n");
- this->LogToDebugFile("X Reg Idx: %x\n", opcode.debug_log.val_reg_index);
- break;
- case DebugLogValueType_MemoryRelAddr:
- this->LogToDebugFile("Val Type: Memory Relative Address\n");
- this->LogToDebugFile("Mem Type: %x\n", opcode.debug_log.mem_type);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.debug_log.rel_address);
- break;
- case DebugLogValueType_MemoryOfsReg:
- this->LogToDebugFile("Val Type: Memory Offset Register\n");
- this->LogToDebugFile("Mem Type: %x\n", opcode.debug_log.mem_type);
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index);
- break;
- case DebugLogValueType_RegisterRelAddr:
- this->LogToDebugFile("Val Type: Register Relative Address\n");
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.debug_log.rel_address);
- break;
- case DebugLogValueType_RegisterOfsReg:
- this->LogToDebugFile("Val Type: Register Offset Register\n");
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index);
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index);
- break;
- }
- default:
- this->LogToDebugFile("Unknown opcode: %x\n", opcode.opcode);
- break;
- }
- }
- DmntCheatVm::Callbacks::~Callbacks() = default;
- bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
- /* If we've ever seen a decode failure, return false. */
- bool valid = this->decode_success;
- CheatVmOpcode opcode = {};
- SCOPE_EXIT({
- this->decode_success &= valid;
- if (valid) {
- out = opcode;
- }
- });
- /* Helper function for getting instruction dwords. */
- auto GetNextDword = [&]() {
- if (this->instruction_ptr >= this->num_opcodes) {
- valid = false;
- return static_cast<u32>(0);
- }
- return this->program[this->instruction_ptr++];
- };
- /* Helper function for parsing a VmInt. */
- auto GetNextVmInt = [&](const u32 bit_width) {
- VmInt val = {0};
- const u32 first_dword = GetNextDword();
- switch (bit_width) {
- case 1:
- val.bit8 = (u8)first_dword;
- break;
- case 2:
- val.bit16 = (u16)first_dword;
- break;
- case 4:
- val.bit32 = first_dword;
- break;
- case 8:
- val.bit64 = (((u64)first_dword) << 32ul) | ((u64)GetNextDword());
- break;
- }
- return val;
- };
- /* Read opcode. */
- const u32 first_dword = GetNextDword();
- if (!valid) {
- return valid;
- }
- opcode.opcode = (CheatVmOpcodeType)(((first_dword >> 28) & 0xF));
- if (opcode.opcode >= CheatVmOpcodeType_ExtendedWidth) {
- opcode.opcode =
- (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 24) & 0xF));
- }
- if (opcode.opcode >= CheatVmOpcodeType_DoubleExtendedWidth) {
- opcode.opcode =
- (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 20) & 0xF));
- }
- /* detect condition start. */
- switch (opcode.opcode) {
- case CheatVmOpcodeType_BeginConditionalBlock:
- case CheatVmOpcodeType_BeginKeypressConditionalBlock:
- case CheatVmOpcodeType_BeginRegisterConditionalBlock:
- opcode.begin_conditional_block = true;
- break;
- default:
- opcode.begin_conditional_block = false;
- break;
- }
- switch (opcode.opcode) {
- case CheatVmOpcodeType_StoreStatic: {
- /* 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */
- /* Read additional words. */
- const u32 second_dword = GetNextDword();
- opcode.store_static.bit_width = (first_dword >> 24) & 0xF;
- opcode.store_static.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
- opcode.store_static.offset_register = ((first_dword >> 16) & 0xF);
- opcode.store_static.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
- opcode.store_static.value = GetNextVmInt(opcode.store_static.bit_width);
- } break;
- case CheatVmOpcodeType_BeginConditionalBlock: {
- /* 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */
- /* Read additional words. */
- const u32 second_dword = GetNextDword();
- opcode.begin_cond.bit_width = (first_dword >> 24) & 0xF;
- opcode.begin_cond.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
- opcode.begin_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF);
- opcode.begin_cond.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
- opcode.begin_cond.value = GetNextVmInt(opcode.store_static.bit_width);
- } break;
- case CheatVmOpcodeType_EndConditionalBlock: {
- /* 20000000 */
- /* There's actually nothing left to process here! */
- } break;
- case CheatVmOpcodeType_ControlLoop: {
- /* 300R0000 VVVVVVVV */
- /* 310R0000 */
- /* Parse register, whether loop start or loop end. */
- opcode.ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
- opcode.ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
- /* Read number of iters if loop start. */
- if (opcode.ctrl_loop.start_loop) {
- opcode.ctrl_loop.num_iters = GetNextDword();
- }
- } break;
- case CheatVmOpcodeType_LoadRegisterStatic: {
- /* 400R0000 VVVVVVVV VVVVVVVV */
- /* Read additional words. */
- opcode.ldr_static.reg_index = ((first_dword >> 16) & 0xF);
- opcode.ldr_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword());
- } break;
- case CheatVmOpcodeType_LoadRegisterMemory: {
- /* 5TMRI0AA AAAAAAAA */
- /* Read additional words. */
- const u32 second_dword = GetNextDword();
- opcode.ldr_memory.bit_width = (first_dword >> 24) & 0xF;
- opcode.ldr_memory.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
- opcode.ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
- opcode.ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
- opcode.ldr_memory.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
- } break;
- case CheatVmOpcodeType_StoreStaticToAddress: {
- /* 6T0RIor0 VVVVVVVV VVVVVVVV */
- /* Read additional words. */
- opcode.str_static.bit_width = (first_dword >> 24) & 0xF;
- opcode.str_static.reg_index = ((first_dword >> 16) & 0xF);
- opcode.str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
- opcode.str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
- opcode.str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
- opcode.str_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword());
- } break;
- case CheatVmOpcodeType_PerformArithmeticStatic: {
- /* 7T0RC000 VVVVVVVV */
- /* Read additional words. */
- opcode.perform_math_static.bit_width = (first_dword >> 24) & 0xF;
- opcode.perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
- opcode.perform_math_static.math_type = (RegisterArithmeticType)((first_dword >> 12) & 0xF);
- opcode.perform_math_static.value = GetNextDword();
- } break;
- case CheatVmOpcodeType_BeginKeypressConditionalBlock: {
- /* 8kkkkkkk */
- /* Just parse the mask. */
- opcode.begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
- } break;
- case CheatVmOpcodeType_PerformArithmeticRegister: {
- /* 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) */
- opcode.perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
- opcode.perform_math_reg.math_type = (RegisterArithmeticType)((first_dword >> 20) & 0xF);
- opcode.perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
- opcode.perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
- opcode.perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
- if (opcode.perform_math_reg.has_immediate) {
- opcode.perform_math_reg.src_reg_2_index = 0;
- opcode.perform_math_reg.value = GetNextVmInt(opcode.perform_math_reg.bit_width);
- } else {
- opcode.perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
- }
- } break;
- case CheatVmOpcodeType_StoreRegisterToAddress: {
- /* ATSRIOxa (aaaaaaaa) */
- /* A = opcode 10 */
- /* T = bit width */
- /* S = src register index */
- /* R = address register index */
- /* I = 1 if increment address register, 0 if not increment address register */
- /* O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region,
- 4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region +
- Relative Address */
- /* x = offset register (for offset type 1), memory type (for offset type 3) */
- /* a = relative address (for offset type 2+3) */
- opcode.str_register.bit_width = (first_dword >> 24) & 0xF;
- opcode.str_register.str_reg_index = ((first_dword >> 20) & 0xF);
- opcode.str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
- opcode.str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
- opcode.str_register.ofs_type = (StoreRegisterOffsetType)(((first_dword >> 8) & 0xF));
- opcode.str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
- switch (opcode.str_register.ofs_type) {
- case StoreRegisterOffsetType_None:
- case StoreRegisterOffsetType_Reg:
- /* Nothing more to do */
- break;
- case StoreRegisterOffsetType_Imm:
- opcode.str_register.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
- break;
- case StoreRegisterOffsetType_MemReg:
- opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- break;
- case StoreRegisterOffsetType_MemImm:
- case StoreRegisterOffsetType_MemImmReg:
- opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.str_register.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
- break;
- default:
- opcode.str_register.ofs_type = StoreRegisterOffsetType_None;
- break;
- }
- } break;
- case CheatVmOpcodeType_BeginRegisterConditionalBlock: {
- /* C0TcSX## */
- /* C0TcS0Ma aaaaaaaa */
- /* C0TcS1Mr */
- /* C0TcS2Ra aaaaaaaa */
- /* C0TcS3Rr */
- /* C0TcS400 VVVVVVVV (VVVVVVVV) */
- /* C0TcS5X0 */
- /* C0 = opcode 0xC0 */
- /* T = bit width */
- /* c = condition type. */
- /* S = source register. */
- /* X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
- * register, */
- /* 2 = register with relative offset, 3 = register with offset register, 4 = static
- * value, 5 = other register. */
- /* M = memory type. */
- /* R = address register. */
- /* a = relative address. */
- /* r = offset register. */
- /* X = other register. */
- /* V = value. */
- opcode.begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
- opcode.begin_reg_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF);
- opcode.begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
- opcode.begin_reg_cond.comp_type = (CompareRegisterValueType)((first_dword >> 8) & 0xF);
- switch (opcode.begin_reg_cond.comp_type) {
- case CompareRegisterValueType_StaticValue:
- opcode.begin_reg_cond.value = GetNextVmInt(opcode.begin_reg_cond.bit_width);
- break;
- case CompareRegisterValueType_OtherRegister:
- opcode.begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
- break;
- case CompareRegisterValueType_MemoryRelAddr:
- opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.begin_reg_cond.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
- break;
- case CompareRegisterValueType_MemoryOfsReg:
- opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
- break;
- case CompareRegisterValueType_RegisterRelAddr:
- opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
- opcode.begin_reg_cond.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
- break;
- case CompareRegisterValueType_RegisterOfsReg:
- opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
- opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
- break;
- }
- } break;
- case CheatVmOpcodeType_SaveRestoreRegister: {
- /* C10D0Sx0 */
- /* C1 = opcode 0xC1 */
- /* D = destination index. */
- /* S = source index. */
- /* x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
- * a register. */
- /* NOTE: If we add more save slots later, current encoding is backwards compatible. */
- opcode.save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
- opcode.save_restore_reg.src_index = (first_dword >> 8) & 0xF;
- opcode.save_restore_reg.op_type = (SaveRestoreRegisterOpType)((first_dword >> 4) & 0xF);
- } break;
- case CheatVmOpcodeType_SaveRestoreRegisterMask: {
- /* C2x0XXXX */
- /* C2 = opcode 0xC2 */
- /* x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. */
- /* X = 16-bit bitmask, bit i --> save or restore register i. */
- opcode.save_restore_regmask.op_type =
- (SaveRestoreRegisterOpType)((first_dword >> 20) & 0xF);
- for (size_t i = 0; i < NumRegisters; i++) {
- opcode.save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
- }
- } break;
- case CheatVmOpcodeType_DebugLog: {
- /* FFFTIX## */
- /* FFFTI0Ma aaaaaaaa */
- /* FFFTI1Mr */
- /* FFFTI2Ra aaaaaaaa */
- /* FFFTI3Rr */
- /* FFFTI4X0 */
- /* FFF = opcode 0xFFF */
- /* T = bit width. */
- /* I = log id. */
- /* X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
- * register, */
- /* 2 = register with relative offset, 3 = register with offset register, 4 = register
- * value. */
- /* M = memory type. */
- /* R = address register. */
- /* a = relative address. */
- /* r = offset register. */
- /* X = value register. */
- opcode.debug_log.bit_width = (first_dword >> 16) & 0xF;
- opcode.debug_log.log_id = ((first_dword >> 12) & 0xF);
- opcode.debug_log.val_type = (DebugLogValueType)((first_dword >> 8) & 0xF);
- switch (opcode.debug_log.val_type) {
- case DebugLogValueType_RegisterValue:
- opcode.debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
- break;
- case DebugLogValueType_MemoryRelAddr:
- opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.debug_log.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
- break;
- case DebugLogValueType_MemoryOfsReg:
- opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.debug_log.ofs_reg_index = (first_dword & 0xF);
- break;
- case DebugLogValueType_RegisterRelAddr:
- opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
- opcode.debug_log.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
- break;
- case DebugLogValueType_RegisterOfsReg:
- opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
- opcode.debug_log.ofs_reg_index = (first_dword & 0xF);
- break;
- }
- } break;
- case CheatVmOpcodeType_ExtendedWidth:
- case CheatVmOpcodeType_DoubleExtendedWidth:
- default:
- /* Unrecognized instruction cannot be decoded. */
- valid = false;
- break;
- }
- /* End decoding. */
- return valid;
- }
- void DmntCheatVm::SkipConditionalBlock() {
- if (this->condition_depth > 0) {
- /* We want to continue until we're out of the current block. */
- const size_t desired_depth = this->condition_depth - 1;
- CheatVmOpcode skip_opcode{};
- while (this->condition_depth > desired_depth && this->DecodeNextOpcode(skip_opcode)) {
- /* Decode instructions until we see end of the current conditional block. */
- /* NOTE: This is broken in gateway's implementation. */
- /* Gateway currently checks for "0x2" instead of "0x20000000" */
- /* In addition, they do a linear scan instead of correctly decoding opcodes. */
- /* This causes issues if "0x2" appears as an immediate in the conditional block... */
- /* We also support nesting of conditional blocks, and Gateway does not. */
- if (skip_opcode.begin_conditional_block) {
- this->condition_depth++;
- } else if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) {
- this->condition_depth--;
- }
- }
- } else {
- /* Skipping, but this->condition_depth = 0. */
- /* This is an error condition. */
- /* However, I don't actually believe it is possible for this to happen. */
- /* I guess we'll throw a fatal error here, so as to encourage me to fix the VM */
- /* in the event that someone triggers it? I don't know how you'd do that. */
- UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM");
- }
- }
- u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
- switch (bit_width) {
- case 1:
- return value.bit8;
- case 2:
- return value.bit16;
- case 4:
- return value.bit32;
- case 8:
- return value.bit64;
- default:
- /* Invalid bit width -> return 0. */
- return 0;
- }
- }
- u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
- MemoryAccessType mem_type, u64 rel_address) {
- switch (mem_type) {
- case MemoryAccessType_MainNso:
- default:
- return metadata.main_nso_extents.base + rel_address;
- case MemoryAccessType_Heap:
- return metadata.heap_extents.base + rel_address;
- }
- }
- void DmntCheatVm::ResetState() {
- for (size_t i = 0; i < DmntCheatVm::NumRegisters; i++) {
- this->registers[i] = 0;
- this->saved_values[i] = 0;
- this->loop_tops[i] = 0;
- }
- this->instruction_ptr = 0;
- this->condition_depth = 0;
- this->decode_success = true;
- }
- bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
- /* Reset opcode count. */
- this->num_opcodes = 0;
- for (size_t i = 0; i < entries.size(); i++) {
- if (entries[i].enabled) {
- /* Bounds check. */
- if (entries[i].definition.num_opcodes + this->num_opcodes > MaximumProgramOpcodeCount) {
- this->num_opcodes = 0;
- return false;
- }
- for (size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
- this->program[this->num_opcodes++] = entries[i].definition.opcodes[n];
- }
- }
- }
- return true;
- }
- void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
- CheatVmOpcode cur_opcode{};
- /* Get Keys down. */
- u64 kDown = callbacks->HidKeysDown();
- this->LogToDebugFile("Started VM execution.\n");
- this->LogToDebugFile("Main NSO: %012lx\n", metadata.main_nso_extents.base);
- this->LogToDebugFile("Heap: %012lx\n", metadata.main_nso_extents.base);
- this->LogToDebugFile("Keys Down: %08x\n", (u32)(kDown & 0x0FFFFFFF));
- /* Clear VM state. */
- this->ResetState();
- /* Loop until program finishes. */
- while (this->DecodeNextOpcode(cur_opcode)) {
- this->LogToDebugFile("Instruction Ptr: %04x\n", (u32)this->instruction_ptr);
- for (size_t i = 0; i < NumRegisters; i++) {
- this->LogToDebugFile("Registers[%02x]: %016lx\n", i, this->registers[i]);
- }
- for (size_t i = 0; i < NumRegisters; i++) {
- this->LogToDebugFile("SavedRegs[%02x]: %016lx\n", i, this->saved_values[i]);
- }
- this->LogOpcode(cur_opcode);
- /* Increment conditional depth, if relevant. */
- if (cur_opcode.begin_conditional_block) {
- this->condition_depth++;
- }
- switch (cur_opcode.opcode) {
- case CheatVmOpcodeType_StoreStatic: {
- /* Calculate address, write value to memory. */
- u64 dst_address = GetCheatProcessAddress(
- metadata, cur_opcode.store_static.mem_type,
- cur_opcode.store_static.rel_address +
- this->registers[cur_opcode.store_static.offset_register]);
- u64 dst_value =
- GetVmInt(cur_opcode.store_static.value, cur_opcode.store_static.bit_width);
- switch (cur_opcode.store_static.bit_width) {
- case 1:
- case 2:
- case 4:
- case 8:
- callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.store_static.bit_width);
- break;
- }
- } break;
- case CheatVmOpcodeType_BeginConditionalBlock: {
- /* Read value from memory. */
- u64 src_address = GetCheatProcessAddress(metadata, cur_opcode.begin_cond.mem_type,
- cur_opcode.begin_cond.rel_address);
- u64 src_value = 0;
- switch (cur_opcode.store_static.bit_width) {
- case 1:
- case 2:
- case 4:
- case 8:
- callbacks->MemoryRead(src_address, &src_value, cur_opcode.begin_cond.bit_width);
- break;
- }
- /* Check against condition. */
- u64 cond_value = GetVmInt(cur_opcode.begin_cond.value, cur_opcode.begin_cond.bit_width);
- bool cond_met = false;
- switch (cur_opcode.begin_cond.cond_type) {
- case ConditionalComparisonType_GT:
- cond_met = src_value > cond_value;
- break;
- case ConditionalComparisonType_GE:
- cond_met = src_value >= cond_value;
- break;
- case ConditionalComparisonType_LT:
- cond_met = src_value < cond_value;
- break;
- case ConditionalComparisonType_LE:
- cond_met = src_value <= cond_value;
- break;
- case ConditionalComparisonType_EQ:
- cond_met = src_value == cond_value;
- break;
- case ConditionalComparisonType_NE:
- cond_met = src_value != cond_value;
- break;
- }
- /* Skip conditional block if condition not met. */
- if (!cond_met) {
- this->SkipConditionalBlock();
- }
- } break;
- case CheatVmOpcodeType_EndConditionalBlock:
- /* Decrement the condition depth. */
- /* We will assume, graciously, that mismatched conditional block ends are a nop. */
- if (this->condition_depth > 0) {
- this->condition_depth--;
- }
- break;
- case CheatVmOpcodeType_ControlLoop:
- if (cur_opcode.ctrl_loop.start_loop) {
- /* Start a loop. */
- this->registers[cur_opcode.ctrl_loop.reg_index] = cur_opcode.ctrl_loop.num_iters;
- this->loop_tops[cur_opcode.ctrl_loop.reg_index] = this->instruction_ptr;
- } else {
- /* End a loop. */
- this->registers[cur_opcode.ctrl_loop.reg_index]--;
- if (this->registers[cur_opcode.ctrl_loop.reg_index] != 0) {
- this->instruction_ptr = this->loop_tops[cur_opcode.ctrl_loop.reg_index];
- }
- }
- break;
- case CheatVmOpcodeType_LoadRegisterStatic:
- /* Set a register to a static value. */
- this->registers[cur_opcode.ldr_static.reg_index] = cur_opcode.ldr_static.value;
- break;
- case CheatVmOpcodeType_LoadRegisterMemory: {
- /* Choose source address. */
- u64 src_address;
- if (cur_opcode.ldr_memory.load_from_reg) {
- src_address = this->registers[cur_opcode.ldr_memory.reg_index] +
- cur_opcode.ldr_memory.rel_address;
- } else {
- src_address = GetCheatProcessAddress(metadata, cur_opcode.ldr_memory.mem_type,
- cur_opcode.ldr_memory.rel_address);
- }
- /* Read into register. Gateway only reads on valid bitwidth. */
- switch (cur_opcode.ldr_memory.bit_width) {
- case 1:
- case 2:
- case 4:
- case 8:
- callbacks->MemoryRead(src_address,
- &this->registers[cur_opcode.ldr_memory.reg_index],
- cur_opcode.ldr_memory.bit_width);
- break;
- }
- } break;
- case CheatVmOpcodeType_StoreStaticToAddress: {
- /* Calculate address. */
- u64 dst_address = this->registers[cur_opcode.str_static.reg_index];
- u64 dst_value = cur_opcode.str_static.value;
- if (cur_opcode.str_static.add_offset_reg) {
- dst_address += this->registers[cur_opcode.str_static.offset_reg_index];
- }
- /* Write value to memory. Gateway only writes on valid bitwidth. */
- switch (cur_opcode.str_static.bit_width) {
- case 1:
- case 2:
- case 4:
- case 8:
- callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.str_static.bit_width);
- break;
- }
- /* Increment register if relevant. */
- if (cur_opcode.str_static.increment_reg) {
- this->registers[cur_opcode.str_static.reg_index] += cur_opcode.str_static.bit_width;
- }
- } break;
- case CheatVmOpcodeType_PerformArithmeticStatic: {
- /* Do requested math. */
- switch (cur_opcode.perform_math_static.math_type) {
- case RegisterArithmeticType_Addition:
- this->registers[cur_opcode.perform_math_static.reg_index] +=
- (u64)cur_opcode.perform_math_static.value;
- break;
- case RegisterArithmeticType_Subtraction:
- this->registers[cur_opcode.perform_math_static.reg_index] -=
- (u64)cur_opcode.perform_math_static.value;
- break;
- case RegisterArithmeticType_Multiplication:
- this->registers[cur_opcode.perform_math_static.reg_index] *=
- (u64)cur_opcode.perform_math_static.value;
- break;
- case RegisterArithmeticType_LeftShift:
- this->registers[cur_opcode.perform_math_static.reg_index] <<=
- (u64)cur_opcode.perform_math_static.value;
- break;
- case RegisterArithmeticType_RightShift:
- this->registers[cur_opcode.perform_math_static.reg_index] >>=
- (u64)cur_opcode.perform_math_static.value;
- break;
- default:
- /* Do not handle extensions here. */
- break;
- }
- /* Apply bit width. */
- switch (cur_opcode.perform_math_static.bit_width) {
- case 1:
- this->registers[cur_opcode.perform_math_static.reg_index] =
- static_cast<u8>(this->registers[cur_opcode.perform_math_static.reg_index]);
- break;
- case 2:
- this->registers[cur_opcode.perform_math_static.reg_index] =
- static_cast<u16>(this->registers[cur_opcode.perform_math_static.reg_index]);
- break;
- case 4:
- this->registers[cur_opcode.perform_math_static.reg_index] =
- static_cast<u32>(this->registers[cur_opcode.perform_math_static.reg_index]);
- break;
- case 8:
- this->registers[cur_opcode.perform_math_static.reg_index] =
- static_cast<u64>(this->registers[cur_opcode.perform_math_static.reg_index]);
- break;
- }
- } break;
- case CheatVmOpcodeType_BeginKeypressConditionalBlock:
- /* Check for keypress. */
- if ((cur_opcode.begin_keypress_cond.key_mask & kDown) !=
- cur_opcode.begin_keypress_cond.key_mask) {
- /* Keys not pressed. Skip conditional block. */
- this->SkipConditionalBlock();
- }
- break;
- case CheatVmOpcodeType_PerformArithmeticRegister: {
- const u64 operand_1_value =
- this->registers[cur_opcode.perform_math_reg.src_reg_1_index];
- const u64 operand_2_value =
- cur_opcode.perform_math_reg.has_immediate
- ? GetVmInt(cur_opcode.perform_math_reg.value,
- cur_opcode.perform_math_reg.bit_width)
- : this->registers[cur_opcode.perform_math_reg.src_reg_2_index];
- u64 res_val = 0;
- /* Do requested math. */
- switch (cur_opcode.perform_math_reg.math_type) {
- case RegisterArithmeticType_Addition:
- res_val = operand_1_value + operand_2_value;
- break;
- case RegisterArithmeticType_Subtraction:
- res_val = operand_1_value - operand_2_value;
- break;
- case RegisterArithmeticType_Multiplication:
- res_val = operand_1_value * operand_2_value;
- break;
- case RegisterArithmeticType_LeftShift:
- res_val = operand_1_value << operand_2_value;
- break;
- case RegisterArithmeticType_RightShift:
- res_val = operand_1_value >> operand_2_value;
- break;
- case RegisterArithmeticType_LogicalAnd:
- res_val = operand_1_value & operand_2_value;
- break;
- case RegisterArithmeticType_LogicalOr:
- res_val = operand_1_value | operand_2_value;
- break;
- case RegisterArithmeticType_LogicalNot:
- res_val = ~operand_1_value;
- break;
- case RegisterArithmeticType_LogicalXor:
- res_val = operand_1_value ^ operand_2_value;
- break;
- case RegisterArithmeticType_None:
- res_val = operand_1_value;
- break;
- }
- /* Apply bit width. */
- switch (cur_opcode.perform_math_reg.bit_width) {
- case 1:
- res_val = static_cast<u8>(res_val);
- break;
- case 2:
- res_val = static_cast<u16>(res_val);
- break;
- case 4:
- res_val = static_cast<u32>(res_val);
- break;
- case 8:
- res_val = static_cast<u64>(res_val);
- break;
- }
- /* Save to register. */
- this->registers[cur_opcode.perform_math_reg.dst_reg_index] = res_val;
- } break;
- case CheatVmOpcodeType_StoreRegisterToAddress: {
- /* Calculate address. */
- u64 dst_value = this->registers[cur_opcode.str_register.str_reg_index];
- u64 dst_address = this->registers[cur_opcode.str_register.addr_reg_index];
- switch (cur_opcode.str_register.ofs_type) {
- case StoreRegisterOffsetType_None:
- /* Nothing more to do */
- break;
- case StoreRegisterOffsetType_Reg:
- dst_address += this->registers[cur_opcode.str_register.ofs_reg_index];
- break;
- case StoreRegisterOffsetType_Imm:
- dst_address += cur_opcode.str_register.rel_address;
- break;
- case StoreRegisterOffsetType_MemReg:
- dst_address =
- GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
- this->registers[cur_opcode.str_register.addr_reg_index]);
- break;
- case StoreRegisterOffsetType_MemImm:
- dst_address = GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
- cur_opcode.str_register.rel_address);
- break;
- case StoreRegisterOffsetType_MemImmReg:
- dst_address =
- GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
- this->registers[cur_opcode.str_register.addr_reg_index] +
- cur_opcode.str_register.rel_address);
- break;
- }
- /* Write value to memory. Write only on valid bitwidth. */
- switch (cur_opcode.str_register.bit_width) {
- case 1:
- case 2:
- case 4:
- case 8:
- callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.str_register.bit_width);
- break;
- }
- /* Increment register if relevant. */
- if (cur_opcode.str_register.increment_reg) {
- this->registers[cur_opcode.str_register.addr_reg_index] +=
- cur_opcode.str_register.bit_width;
- }
- } break;
- case CheatVmOpcodeType_BeginRegisterConditionalBlock: {
- /* Get value from register. */
- u64 src_value = 0;
- switch (cur_opcode.begin_reg_cond.bit_width) {
- case 1:
- src_value = static_cast<u8>(
- this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFul);
- break;
- case 2:
- src_value = static_cast<u16>(
- this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFul);
- break;
- case 4:
- src_value = static_cast<u32>(
- this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFFFFFul);
- break;
- case 8:
- src_value =
- static_cast<u64>(this->registers[cur_opcode.begin_reg_cond.val_reg_index] &
- 0xFFFFFFFFFFFFFFFFul);
- break;
- }
- /* Read value from memory. */
- u64 cond_value = 0;
- if (cur_opcode.begin_reg_cond.comp_type == CompareRegisterValueType_StaticValue) {
- cond_value =
- GetVmInt(cur_opcode.begin_reg_cond.value, cur_opcode.begin_reg_cond.bit_width);
- } else if (cur_opcode.begin_reg_cond.comp_type ==
- CompareRegisterValueType_OtherRegister) {
- switch (cur_opcode.begin_reg_cond.bit_width) {
- case 1:
- cond_value = static_cast<u8>(
- this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFul);
- break;
- case 2:
- cond_value = static_cast<u16>(
- this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFul);
- break;
- case 4:
- cond_value = static_cast<u32>(
- this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFFFFFul);
- break;
- case 8:
- cond_value = static_cast<u64>(
- this->registers[cur_opcode.begin_reg_cond.other_reg_index] &
- 0xFFFFFFFFFFFFFFFFul);
- break;
- }
- } else {
- u64 cond_address = 0;
- switch (cur_opcode.begin_reg_cond.comp_type) {
- case CompareRegisterValueType_MemoryRelAddr:
- cond_address =
- GetCheatProcessAddress(metadata, cur_opcode.begin_reg_cond.mem_type,
- cur_opcode.begin_reg_cond.rel_address);
- break;
- case CompareRegisterValueType_MemoryOfsReg:
- cond_address = GetCheatProcessAddress(
- metadata, cur_opcode.begin_reg_cond.mem_type,
- this->registers[cur_opcode.begin_reg_cond.ofs_reg_index]);
- break;
- case CompareRegisterValueType_RegisterRelAddr:
- cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] +
- cur_opcode.begin_reg_cond.rel_address;
- break;
- case CompareRegisterValueType_RegisterOfsReg:
- cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] +
- this->registers[cur_opcode.begin_reg_cond.ofs_reg_index];
- break;
- default:
- break;
- }
- switch (cur_opcode.begin_reg_cond.bit_width) {
- case 1:
- case 2:
- case 4:
- case 8:
- callbacks->MemoryRead(cond_address, &cond_value,
- cur_opcode.begin_reg_cond.bit_width);
- break;
- }
- }
- /* Check against condition. */
- bool cond_met = false;
- switch (cur_opcode.begin_reg_cond.cond_type) {
- case ConditionalComparisonType_GT:
- cond_met = src_value > cond_value;
- break;
- case ConditionalComparisonType_GE:
- cond_met = src_value >= cond_value;
- break;
- case ConditionalComparisonType_LT:
- cond_met = src_value < cond_value;
- break;
- case ConditionalComparisonType_LE:
- cond_met = src_value <= cond_value;
- break;
- case ConditionalComparisonType_EQ:
- cond_met = src_value == cond_value;
- break;
- case ConditionalComparisonType_NE:
- cond_met = src_value != cond_value;
- break;
- }
- /* Skip conditional block if condition not met. */
- if (!cond_met) {
- this->SkipConditionalBlock();
- }
- } break;
- case CheatVmOpcodeType_SaveRestoreRegister:
- /* Save or restore a register. */
- switch (cur_opcode.save_restore_reg.op_type) {
- case SaveRestoreRegisterOpType_ClearRegs:
- this->registers[cur_opcode.save_restore_reg.dst_index] = 0ul;
- break;
- case SaveRestoreRegisterOpType_ClearSaved:
- this->saved_values[cur_opcode.save_restore_reg.dst_index] = 0ul;
- break;
- case SaveRestoreRegisterOpType_Save:
- this->saved_values[cur_opcode.save_restore_reg.dst_index] =
- this->registers[cur_opcode.save_restore_reg.src_index];
- break;
- case SaveRestoreRegisterOpType_Restore:
- default:
- this->registers[cur_opcode.save_restore_reg.dst_index] =
- this->saved_values[cur_opcode.save_restore_reg.src_index];
- break;
- }
- break;
- case CheatVmOpcodeType_SaveRestoreRegisterMask:
- /* Save or restore register mask. */
- u64* src;
- u64* dst;
- switch (cur_opcode.save_restore_regmask.op_type) {
- case SaveRestoreRegisterOpType_ClearSaved:
- case SaveRestoreRegisterOpType_Save:
- src = this->registers.data();
- dst = this->saved_values.data();
- break;
- case SaveRestoreRegisterOpType_ClearRegs:
- case SaveRestoreRegisterOpType_Restore:
- default:
- src = this->registers.data();
- dst = this->saved_values.data();
- break;
- }
- for (size_t i = 0; i < NumRegisters; i++) {
- if (cur_opcode.save_restore_regmask.should_operate[i]) {
- switch (cur_opcode.save_restore_regmask.op_type) {
- case SaveRestoreRegisterOpType_ClearSaved:
- case SaveRestoreRegisterOpType_ClearRegs:
- dst[i] = 0ul;
- break;
- case SaveRestoreRegisterOpType_Save:
- case SaveRestoreRegisterOpType_Restore:
- default:
- dst[i] = src[i];
- break;
- }
- }
- }
- break;
- case CheatVmOpcodeType_DebugLog: {
- /* Read value from memory. */
- u64 log_value = 0;
- if (cur_opcode.debug_log.val_type == DebugLogValueType_RegisterValue) {
- switch (cur_opcode.debug_log.bit_width) {
- case 1:
- log_value = static_cast<u8>(
- this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFul);
- break;
- case 2:
- log_value = static_cast<u16>(
- this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFul);
- break;
- case 4:
- log_value = static_cast<u32>(
- this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFul);
- break;
- case 8:
- log_value = static_cast<u64>(
- this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFFFFFFFFFul);
- break;
- }
- } else {
- u64 val_address = 0;
- switch (cur_opcode.debug_log.val_type) {
- case DebugLogValueType_MemoryRelAddr:
- val_address = GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type,
- cur_opcode.debug_log.rel_address);
- break;
- case DebugLogValueType_MemoryOfsReg:
- val_address =
- GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type,
- this->registers[cur_opcode.debug_log.ofs_reg_index]);
- break;
- case DebugLogValueType_RegisterRelAddr:
- val_address = this->registers[cur_opcode.debug_log.addr_reg_index] +
- cur_opcode.debug_log.rel_address;
- break;
- case DebugLogValueType_RegisterOfsReg:
- val_address = this->registers[cur_opcode.debug_log.addr_reg_index] +
- this->registers[cur_opcode.debug_log.ofs_reg_index];
- break;
- default:
- break;
- }
- switch (cur_opcode.debug_log.bit_width) {
- case 1:
- case 2:
- case 4:
- case 8:
- callbacks->MemoryRead(val_address, &log_value, cur_opcode.debug_log.bit_width);
- break;
- }
- }
- /* Log value. */
- this->DebugLog(cur_opcode.debug_log.log_id, log_value);
- } break;
- default:
- /* By default, we do a no-op. */
- break;
- }
- }
- }
- } // namespace Memory
|