1 /* 2 * Copyright (c) 2018-2019 Atmosphère-NX 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 /* 18 * Adapted by DarkLordZach for use/interaction with yuzu 19 * 20 * Modifications Copyright 2019 yuzu emulator team 21 * Licensed under GPLv2 or any later version 22 * Refer to the license.txt file included. 23 */ 24 25 #pragma once 26 27 #include <variant> 28 #include <vector> 29 #include <fmt/printf.h> 30 #include "common/common_types.h" 31 #include "core/memory/dmnt_cheat_types.h" 32 33 namespace Core::Memory { 34 35 enum class CheatVmOpcodeType : u32 { 36 StoreStatic = 0, 37 BeginConditionalBlock = 1, 38 EndConditionalBlock = 2, 39 ControlLoop = 3, 40 LoadRegisterStatic = 4, 41 LoadRegisterMemory = 5, 42 StoreStaticToAddress = 6, 43 PerformArithmeticStatic = 7, 44 BeginKeypressConditionalBlock = 8, 45 46 // These are not implemented by Gateway's VM. 47 PerformArithmeticRegister = 9, 48 StoreRegisterToAddress = 10, 49 Reserved11 = 11, 50 51 // This is a meta entry, and not a real opcode. 52 // This is to facilitate multi-nybble instruction decoding. 53 ExtendedWidth = 12, 54 55 // Extended width opcodes. 56 BeginRegisterConditionalBlock = 0xC0, 57 SaveRestoreRegister = 0xC1, 58 SaveRestoreRegisterMask = 0xC2, 59 ReadWriteStaticRegister = 0xC3, 60 61 // This is a meta entry, and not a real opcode. 62 // This is to facilitate multi-nybble instruction decoding. 63 DoubleExtendedWidth = 0xF0, 64 65 // Double-extended width opcodes. 66 DebugLog = 0xFFF, 67 }; 68 69 enum class MemoryAccessType : u32 { 70 MainNso = 0, 71 Heap = 1, 72 }; 73 74 enum class ConditionalComparisonType : u32 { 75 GT = 1, 76 GE = 2, 77 LT = 3, 78 LE = 4, 79 EQ = 5, 80 NE = 6, 81 }; 82 83 enum class RegisterArithmeticType : u32 { 84 Addition = 0, 85 Subtraction = 1, 86 Multiplication = 2, 87 LeftShift = 3, 88 RightShift = 4, 89 90 // These are not supported by Gateway's VM. 91 LogicalAnd = 5, 92 LogicalOr = 6, 93 LogicalNot = 7, 94 LogicalXor = 8, 95 96 None = 9, 97 }; 98 99 enum class StoreRegisterOffsetType : u32 { 100 None = 0, 101 Reg = 1, 102 Imm = 2, 103 MemReg = 3, 104 MemImm = 4, 105 MemImmReg = 5, 106 }; 107 108 enum class CompareRegisterValueType : u32 { 109 MemoryRelAddr = 0, 110 MemoryOfsReg = 1, 111 RegisterRelAddr = 2, 112 RegisterOfsReg = 3, 113 StaticValue = 4, 114 OtherRegister = 5, 115 }; 116 117 enum class SaveRestoreRegisterOpType : u32 { 118 Restore = 0, 119 Save = 1, 120 ClearSaved = 2, 121 ClearRegs = 3, 122 }; 123 124 enum class DebugLogValueType : u32 { 125 MemoryRelAddr = 0, 126 MemoryOfsReg = 1, 127 RegisterRelAddr = 2, 128 RegisterOfsReg = 3, 129 RegisterValue = 4, 130 }; 131 132 union VmInt { 133 u8 bit8; 134 u16 bit16; 135 u32 bit32; 136 u64 bit64; 137 }; 138 139 struct StoreStaticOpcode { 140 u32 bit_width{}; 141 MemoryAccessType mem_type{}; 142 u32 offset_register{}; 143 u64 rel_address{}; 144 VmInt value{}; 145 }; 146 147 struct BeginConditionalOpcode { 148 u32 bit_width{}; 149 MemoryAccessType mem_type{}; 150 ConditionalComparisonType cond_type{}; 151 u64 rel_address{}; 152 VmInt value{}; 153 }; 154 155 struct EndConditionalOpcode {}; 156 157 struct ControlLoopOpcode { 158 bool start_loop{}; 159 u32 reg_index{}; 160 u32 num_iters{}; 161 }; 162 163 struct LoadRegisterStaticOpcode { 164 u32 reg_index{}; 165 u64 value{}; 166 }; 167 168 struct LoadRegisterMemoryOpcode { 169 u32 bit_width{}; 170 MemoryAccessType mem_type{}; 171 u32 reg_index{}; 172 bool load_from_reg{}; 173 u64 rel_address{}; 174 }; 175 176 struct StoreStaticToAddressOpcode { 177 u32 bit_width{}; 178 u32 reg_index{}; 179 bool increment_reg{}; 180 bool add_offset_reg{}; 181 u32 offset_reg_index{}; 182 u64 value{}; 183 }; 184 185 struct PerformArithmeticStaticOpcode { 186 u32 bit_width{}; 187 u32 reg_index{}; 188 RegisterArithmeticType math_type{}; 189 u32 value{}; 190 }; 191 192 struct BeginKeypressConditionalOpcode { 193 u32 key_mask{}; 194 }; 195 196 struct PerformArithmeticRegisterOpcode { 197 u32 bit_width{}; 198 RegisterArithmeticType math_type{}; 199 u32 dst_reg_index{}; 200 u32 src_reg_1_index{}; 201 u32 src_reg_2_index{}; 202 bool has_immediate{}; 203 VmInt value{}; 204 }; 205 206 struct StoreRegisterToAddressOpcode { 207 u32 bit_width{}; 208 u32 str_reg_index{}; 209 u32 addr_reg_index{}; 210 bool increment_reg{}; 211 StoreRegisterOffsetType ofs_type{}; 212 MemoryAccessType mem_type{}; 213 u32 ofs_reg_index{}; 214 u64 rel_address{}; 215 }; 216 217 struct BeginRegisterConditionalOpcode { 218 u32 bit_width{}; 219 ConditionalComparisonType cond_type{}; 220 u32 val_reg_index{}; 221 CompareRegisterValueType comp_type{}; 222 MemoryAccessType mem_type{}; 223 u32 addr_reg_index{}; 224 u32 other_reg_index{}; 225 u32 ofs_reg_index{}; 226 u64 rel_address{}; 227 VmInt value{}; 228 }; 229 230 struct SaveRestoreRegisterOpcode { 231 u32 dst_index{}; 232 u32 src_index{}; 233 SaveRestoreRegisterOpType op_type{}; 234 }; 235 236 struct SaveRestoreRegisterMaskOpcode { 237 SaveRestoreRegisterOpType op_type{}; 238 std::array<bool, 0x10> should_operate{}; 239 }; 240 241 struct ReadWriteStaticRegisterOpcode { 242 u32 static_idx{}; 243 u32 idx{}; 244 }; 245 246 struct DebugLogOpcode { 247 u32 bit_width{}; 248 u32 log_id{}; 249 DebugLogValueType val_type{}; 250 MemoryAccessType mem_type{}; 251 u32 addr_reg_index{}; 252 u32 val_reg_index{}; 253 u32 ofs_reg_index{}; 254 u64 rel_address{}; 255 }; 256 257 struct UnrecognizedInstruction { 258 CheatVmOpcodeType opcode{}; 259 }; 260 261 struct CheatVmOpcode { 262 bool begin_conditional_block{}; 263 std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode, 264 LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode, 265 PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, 266 PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, 267 BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, 268 SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode, 269 UnrecognizedInstruction> 270 opcode{}; 271 }; 272 273 class DmntCheatVm { 274 public: 275 /// Helper Type for DmntCheatVm <=> yuzu Interface 276 class Callbacks { 277 public: 278 virtual ~Callbacks(); 279 280 virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; 281 virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; 282 283 virtual u64 HidKeysDown() = 0; 284 285 virtual void DebugLog(u8 id, u64 value) = 0; 286 virtual void CommandLog(std::string_view data) = 0; 287 }; 288 289 static constexpr std::size_t MaximumProgramOpcodeCount = 0x400; 290 static constexpr std::size_t NumRegisters = 0x10; 291 static constexpr std::size_t NumReadableStaticRegisters = 0x80; 292 static constexpr std::size_t NumWritableStaticRegisters = 0x80; 293 static constexpr std::size_t NumStaticRegisters = 294 NumReadableStaticRegisters + NumWritableStaticRegisters; 295 296 explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks); 297 ~DmntCheatVm(); 298 GetProgramSize()299 std::size_t GetProgramSize() const { 300 return this->num_opcodes; 301 } 302 303 bool LoadProgram(const std::vector<CheatEntry>& cheats); 304 void Execute(const CheatProcessMetadata& metadata); 305 306 private: 307 std::unique_ptr<Callbacks> callbacks; 308 309 std::size_t num_opcodes = 0; 310 std::size_t instruction_ptr = 0; 311 std::size_t condition_depth = 0; 312 bool decode_success = false; 313 std::array<u32, MaximumProgramOpcodeCount> program{}; 314 std::array<u64, NumRegisters> registers{}; 315 std::array<u64, NumRegisters> saved_values{}; 316 std::array<u64, NumStaticRegisters> static_registers{}; 317 std::array<std::size_t, NumRegisters> loop_tops{}; 318 319 bool DecodeNextOpcode(CheatVmOpcode& out); 320 void SkipConditionalBlock(); 321 void ResetState(); 322 323 // For implementing the DebugLog opcode. 324 void DebugLog(u32 log_id, u64 value); 325 326 void LogOpcode(const CheatVmOpcode& opcode); 327 328 static u64 GetVmInt(VmInt value, u32 bit_width); 329 static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata, 330 MemoryAccessType mem_type, u64 rel_address); 331 }; 332 333 }; // namespace Core::Memory 334