From 12aa127df3826857149bfc4b787cfb7df3fdcafe Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 30 May 2019 19:34:02 -0400 Subject: memory: Port Atmosphere's DmntCheatVm This was done because the current VM contained many inaccuracies and this also allows cheats to have identical behavior between hardware and yuzu. --- src/core/memory/dmnt_cheat_vm.h | 334 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 src/core/memory/dmnt_cheat_vm.h (limited to 'src/core/memory/dmnt_cheat_vm.h') diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h new file mode 100644 index 000000000..bea451db4 --- /dev/null +++ b/src/core/memory/dmnt_cheat_vm.h @@ -0,0 +1,334 @@ +/* + * 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 . + */ + +/* + * 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. + */ + +#pragma once + +#include +#include +#include "common/common_types.h" +#include "core/memory/dmnt_cheat_types.h" + +namespace Memory { + +enum CheatVmOpcodeType : u32 { + CheatVmOpcodeType_StoreStatic = 0, + CheatVmOpcodeType_BeginConditionalBlock = 1, + CheatVmOpcodeType_EndConditionalBlock = 2, + CheatVmOpcodeType_ControlLoop = 3, + CheatVmOpcodeType_LoadRegisterStatic = 4, + CheatVmOpcodeType_LoadRegisterMemory = 5, + CheatVmOpcodeType_StoreStaticToAddress = 6, + CheatVmOpcodeType_PerformArithmeticStatic = 7, + CheatVmOpcodeType_BeginKeypressConditionalBlock = 8, + + /* These are not implemented by Gateway's VM. */ + CheatVmOpcodeType_PerformArithmeticRegister = 9, + CheatVmOpcodeType_StoreRegisterToAddress = 10, + CheatVmOpcodeType_Reserved11 = 11, + + /* This is a meta entry, and not a real opcode. */ + /* This is to facilitate multi-nybble instruction decoding. */ + CheatVmOpcodeType_ExtendedWidth = 12, + + /* Extended width opcodes. */ + CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0, + CheatVmOpcodeType_SaveRestoreRegister = 0xC1, + CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2, + + /* This is a meta entry, and not a real opcode. */ + /* This is to facilitate multi-nybble instruction decoding. */ + CheatVmOpcodeType_DoubleExtendedWidth = 0xF0, + + /* Double-extended width opcodes. */ + CheatVmOpcodeType_DebugLog = 0xFFF, +}; + +enum MemoryAccessType : u32 { + MemoryAccessType_MainNso = 0, + MemoryAccessType_Heap = 1, +}; + +enum ConditionalComparisonType : u32 { + ConditionalComparisonType_GT = 1, + ConditionalComparisonType_GE = 2, + ConditionalComparisonType_LT = 3, + ConditionalComparisonType_LE = 4, + ConditionalComparisonType_EQ = 5, + ConditionalComparisonType_NE = 6, +}; + +enum RegisterArithmeticType : u32 { + RegisterArithmeticType_Addition = 0, + RegisterArithmeticType_Subtraction = 1, + RegisterArithmeticType_Multiplication = 2, + RegisterArithmeticType_LeftShift = 3, + RegisterArithmeticType_RightShift = 4, + + /* These are not supported by Gateway's VM. */ + RegisterArithmeticType_LogicalAnd = 5, + RegisterArithmeticType_LogicalOr = 6, + RegisterArithmeticType_LogicalNot = 7, + RegisterArithmeticType_LogicalXor = 8, + + RegisterArithmeticType_None = 9, +}; + +enum StoreRegisterOffsetType : u32 { + StoreRegisterOffsetType_None = 0, + StoreRegisterOffsetType_Reg = 1, + StoreRegisterOffsetType_Imm = 2, + StoreRegisterOffsetType_MemReg = 3, + StoreRegisterOffsetType_MemImm = 4, + StoreRegisterOffsetType_MemImmReg = 5, +}; + +enum CompareRegisterValueType : u32 { + CompareRegisterValueType_MemoryRelAddr = 0, + CompareRegisterValueType_MemoryOfsReg = 1, + CompareRegisterValueType_RegisterRelAddr = 2, + CompareRegisterValueType_RegisterOfsReg = 3, + CompareRegisterValueType_StaticValue = 4, + CompareRegisterValueType_OtherRegister = 5, +}; + +enum SaveRestoreRegisterOpType : u32 { + SaveRestoreRegisterOpType_Restore = 0, + SaveRestoreRegisterOpType_Save = 1, + SaveRestoreRegisterOpType_ClearSaved = 2, + SaveRestoreRegisterOpType_ClearRegs = 3, +}; + +enum DebugLogValueType : u32 { + DebugLogValueType_MemoryRelAddr = 0, + DebugLogValueType_MemoryOfsReg = 1, + DebugLogValueType_RegisterRelAddr = 2, + DebugLogValueType_RegisterOfsReg = 3, + DebugLogValueType_RegisterValue = 4, +}; + +union VmInt { + u8 bit8; + u16 bit16; + u32 bit32; + u64 bit64; +}; + +struct StoreStaticOpcode { + u32 bit_width; + MemoryAccessType mem_type; + u32 offset_register; + u64 rel_address; + VmInt value; +}; + +struct BeginConditionalOpcode { + u32 bit_width; + MemoryAccessType mem_type; + ConditionalComparisonType cond_type; + u64 rel_address; + VmInt value; +}; + +struct EndConditionalOpcode {}; + +struct ControlLoopOpcode { + bool start_loop; + u32 reg_index; + u32 num_iters; +}; + +struct LoadRegisterStaticOpcode { + u32 reg_index; + u64 value; +}; + +struct LoadRegisterMemoryOpcode { + u32 bit_width; + MemoryAccessType mem_type; + u32 reg_index; + bool load_from_reg; + u64 rel_address; +}; + +struct StoreStaticToAddressOpcode { + u32 bit_width; + u32 reg_index; + bool increment_reg; + bool add_offset_reg; + u32 offset_reg_index; + u64 value; +}; + +struct PerformArithmeticStaticOpcode { + u32 bit_width; + u32 reg_index; + RegisterArithmeticType math_type; + u32 value; +}; + +struct BeginKeypressConditionalOpcode { + u32 key_mask; +}; + +struct PerformArithmeticRegisterOpcode { + u32 bit_width; + RegisterArithmeticType math_type; + u32 dst_reg_index; + u32 src_reg_1_index; + u32 src_reg_2_index; + bool has_immediate; + VmInt value; +}; + +struct StoreRegisterToAddressOpcode { + u32 bit_width; + u32 str_reg_index; + u32 addr_reg_index; + bool increment_reg; + StoreRegisterOffsetType ofs_type; + MemoryAccessType mem_type; + u32 ofs_reg_index; + u64 rel_address; +}; + +struct BeginRegisterConditionalOpcode { + u32 bit_width; + ConditionalComparisonType cond_type; + u32 val_reg_index; + CompareRegisterValueType comp_type; + MemoryAccessType mem_type; + u32 addr_reg_index; + u32 other_reg_index; + u32 ofs_reg_index; + u64 rel_address; + VmInt value; +}; + +struct SaveRestoreRegisterOpcode { + u32 dst_index; + u32 src_index; + SaveRestoreRegisterOpType op_type; +}; + +struct SaveRestoreRegisterMaskOpcode { + SaveRestoreRegisterOpType op_type; + std::array should_operate; +}; + +struct DebugLogOpcode { + u32 bit_width; + u32 log_id; + DebugLogValueType val_type; + MemoryAccessType mem_type; + u32 addr_reg_index; + u32 val_reg_index; + u32 ofs_reg_index; + u64 rel_address; +}; + +struct CheatVmOpcode { + CheatVmOpcodeType opcode; + bool begin_conditional_block; + union { + StoreStaticOpcode store_static; + BeginConditionalOpcode begin_cond; + EndConditionalOpcode end_cond; + ControlLoopOpcode ctrl_loop; + LoadRegisterStaticOpcode ldr_static; + LoadRegisterMemoryOpcode ldr_memory; + StoreStaticToAddressOpcode str_static; + PerformArithmeticStaticOpcode perform_math_static; + BeginKeypressConditionalOpcode begin_keypress_cond; + PerformArithmeticRegisterOpcode perform_math_reg; + StoreRegisterToAddressOpcode str_register; + BeginRegisterConditionalOpcode begin_reg_cond; + SaveRestoreRegisterOpcode save_restore_reg; + SaveRestoreRegisterMaskOpcode save_restore_regmask; + DebugLogOpcode debug_log; + }; +}; + +class DmntCheatVm { +public: + /// Helper Type for DmntCheatVm <=> yuzu Interface + class Callbacks { + public: + virtual ~Callbacks(); + + virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; + virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; + + virtual u64 HidKeysDown() = 0; + + virtual void DebugLog(u8 id, u64 value) = 0; + virtual void CommandLog(std::string_view data) = 0; + }; + + constexpr static size_t MaximumProgramOpcodeCount = 0x400; + constexpr static size_t NumRegisters = 0x10; + +private: + std::unique_ptr callbacks; + + size_t num_opcodes = 0; + size_t instruction_ptr = 0; + size_t condition_depth = 0; + bool decode_success = false; + std::array program{}; + std::array registers{}; + std::array saved_values{}; + std::array loop_tops{}; + +private: + bool DecodeNextOpcode(CheatVmOpcode& out); + void SkipConditionalBlock(); + void ResetState(); + + /* For implementing the DebugLog opcode. */ + void DebugLog(u32 log_id, u64 value); + + /* For debugging. These will be IFDEF'd out normally. */ + template + void LogToDebugFile(const char* format, const Args&... args) { + callbacks->CommandLog(fmt::sprintf(format, args...)); + } + + void LogOpcode(const CheatVmOpcode& opcode); + + static u64 GetVmInt(VmInt value, u32 bit_width); + static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata, + MemoryAccessType mem_type, u64 rel_address); + +public: + DmntCheatVm(std::unique_ptr callbacks) : callbacks(std::move(callbacks)) {} + + size_t GetProgramSize() { + return this->num_opcodes; + } + + bool LoadProgram(const std::vector& cheats); + void Execute(const CheatProcessMetadata& metadata); +}; + +}; // namespace Memory -- cgit v1.2.3