예제 #1
0
        private const byte Rsh = 4; // reg >>= rhs

        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // 7T0RC000 VVVVVVVV
            // T: Width of arithmetic operation(1, 2, 4, or 8 bytes).
            // R: Register to apply arithmetic to.
            // C: Arithmetic operation to apply, see below.
            // V: Value to use for arithmetic operation.

            byte          operationWidth     = instruction[OperationWidthIndex];
            Register      register           = context.GetRegister(instruction[DestinationRegisterIndex]);
            byte          operation          = instruction[OperationTypeIndex];
            ulong         immediate          = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, ValueImmediateSize);
            Value <ulong> rightHandSideValue = new Value <ulong>(immediate);

            void Emit(Type operationType)
            {
                InstructionHelper.Emit(operationType, operationWidth, context, register, register, rightHandSideValue);
            }

            switch (operation)
            {
            case Add: Emit(typeof(OpAdd <>)); break;

            case Sub: Emit(typeof(OpSub <>)); break;

            case Mul: Emit(typeof(OpMul <>)); break;

            case Lsh: Emit(typeof(OpLsh <>)); break;

            case Rsh: Emit(typeof(OpRsh <>)); break;

            default:
                throw new TamperCompilationException($"Invalid arithmetic operation {operation} in Atmosphere cheat");
            }
        }
예제 #2
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // C3000XXx
            // XX: Static register index, 0x00 to 0x7F for reading or 0x80 to 0xFF for writing.
            // x: Register index.

            ulong    staticRegisterIndex = InstructionHelper.GetImmediate(instruction, StaticRegisterIndex, StaticRegisterSize);
            Register register            = context.GetRegister(instruction[RegisterIndex]);

            IOperand sourceRegister;
            IOperand destinationRegister;

            if (staticRegisterIndex < FirstWriteRegister)
            {
                // Read from static register.
                sourceRegister      = context.GetStaticRegister((byte)staticRegisterIndex);
                destinationRegister = register;
            }
            else
            {
                // Write to static register.
                sourceRegister      = register;
                destinationRegister = context.GetStaticRegister((byte)(staticRegisterIndex - FirstWriteRegister));
            }

            context.CurrentOperations.Add(new OpMov <ulong>(destinationRegister, sourceRegister));
        }
예제 #3
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // 6T0RIor0 VVVVVVVV VVVVVVVV
            // T: Width of memory write(1, 2, 4, or 8 bytes).
            // R: Register used as base memory address.
            // I: Increment register flag(0 = do not increment R, 1 = increment R by T).
            // o: Offset register enable flag(0 = do not add r to address, 1 = add r to address).
            // r: Register used as offset when o is 1.
            // V: Value to write to memory.

            byte          operationWidth           = instruction[OperationWidthIndex];
            Register      sourceRegister           = context.GetRegister(instruction[AddressRegisterIndex]);
            byte          incrementAddressRegister = instruction[IncrementAddressRegisterIndex];
            byte          useOffsetRegister        = instruction[UseOffsetRegisterIndex];
            ulong         immediate  = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, ValueImmediateSize);
            Value <ulong> storeValue = new Value <ulong>(immediate);

            Pointer destinationMemory;

            switch (useOffsetRegister)
            {
            case 0:
                // Don't offset the address register by another register.
                destinationMemory = MemoryHelper.EmitPointer(sourceRegister, context);
                break;

            case 1:
                // Replace the source address by the sum of the base and offset registers.
                Register offsetRegister = context.GetRegister(instruction[OffsetRegisterIndex]);
                destinationMemory = MemoryHelper.EmitPointer(sourceRegister, offsetRegister, context);
                break;

            default:
                throw new TamperCompilationException($"Invalid offset mode {useOffsetRegister} in Atmosphere cheat");
            }

            InstructionHelper.EmitMov(operationWidth, context, destinationMemory, storeValue);

            switch (incrementAddressRegister)
            {
            case 0:
                // Don't increment the address register by operationWidth.
                break;

            case 1:
                // Increment the address register by operationWidth.
                IOperand increment = new Value <ulong>(operationWidth);
                context.CurrentOperations.Add(new OpAdd <ulong>(sourceRegister, sourceRegister, increment));
                break;

            default:
                throw new TamperCompilationException($"Invalid increment mode {incrementAddressRegister} in Atmosphere cheat");
            }
        }
예제 #4
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // 300R0000 VVVVVVVV
            // R: Register to use as loop counter.
            // V: Number of iterations to loop.

            // 310R0000

            byte mode = instruction[StartOrEndIndex];
            byte iterationRegisterIndex = instruction[IterationRegisterIndex];

            switch (mode)
            {
            case LoopBegin:
                // Just start a new compilation block and parse the instruction itself at the end.
                context.BlockStack.Push(new OperationBlock(instruction));
                return;

            case LoopEnd:
                break;

            default:
                throw new TamperCompilationException($"Invalid loop {mode} in Atmosphere cheat");
            }

            // Use the loop begin instruction stored in the stack.
            instruction = context.CurrentBlock.BaseInstruction;
            CodeType codeType = InstructionHelper.GetCodeType(instruction);

            if (codeType != CodeType.StartEndLoop)
            {
                throw new TamperCompilationException($"Loop end does not match code type {codeType} in Atmosphere cheat");
            }

            // Validate if the register in the beginning and end are the same.

            byte oldIterationRegisterIndex = instruction[IterationRegisterIndex];

            if (iterationRegisterIndex != oldIterationRegisterIndex)
            {
                throw new TamperCompilationException($"The register used for the loop changed from {oldIterationRegisterIndex} to {iterationRegisterIndex} in Atmosphere cheat");
            }

            Register iterationRegister = context.GetRegister(iterationRegisterIndex);
            ulong    immediate         = InstructionHelper.GetImmediate(instruction, IterationsImmediateIndex, IterationsImmediateSize);

            // Create a loop block with the current operations and nest it in the upper
            // block of the stack.

            ForBlock block = new ForBlock(immediate, iterationRegister, context.CurrentOperations);

            context.BlockStack.Pop();
            context.CurrentOperations.Add(block);
        }
예제 #5
0
        public static ICondition Emit(byte[] instruction, CompilationContext context)
        {
            // 8kkkkkkk
            // k: Keypad mask to check against, see below.
            // Note that for multiple button combinations, the bitmasks should be ORd together.
            // The Keypad Values are the direct output of hidKeysDown().

            ulong inputMask = InstructionHelper.GetImmediate(instruction, InputMaskIndex, InputMaskSize);

            return(new InputMask((long)inputMask, context.PressedKeys));
        }
예제 #6
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // 400R0000 VVVVVVVV VVVVVVVV
            // R: Register to use.
            // V: Value to load.

            Register      destinationRegister = context.GetRegister(instruction[RegisterIndex]);
            ulong         immediate           = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, ValueImmediateSize);
            Value <ulong> sourceValue         = new Value <ulong>(immediate);

            context.CurrentOperations.Add(new OpMov <ulong>(destinationRegister, sourceValue));
        }
예제 #7
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // C2x0XXXX
            // x: Operand Type, see below.
            // X: 16-bit bitmask, bit i == save or restore register i.

            byte  operationType = instruction[OperationTypeIndex];
            ulong mask          = InstructionHelper.GetImmediate(instruction, RegisterMaskIndex, RegisterMaskSize);

            for (byte regIndex = 0; mask != 0; mask >>= 1, regIndex++)
            {
                if ((mask & 0x1) != 0)
                {
                    SaveOrRestoreRegister.Impl(operationType, regIndex, regIndex, context);
                }
            }
        }
예제 #8
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // 5TMR00AA AAAAAAAA
            // T: Width of memory read (1, 2, 4, or 8 bytes).
            // M: Memory region to write to (0 = Main NSO, 1 = Heap).
            // R: Register to load value into.
            // A: Immediate offset to use from memory region base.

            // 5TMR10AA AAAAAAAA
            // T: Width of memory read(1, 2, 4, or 8 bytes).
            // M: Ignored.
            // R: Register to use as base address and to load value into.
            // A: Immediate offset to use from register R.

            byte         operationWidth              = instruction[OperationWidthIndex];
            MemoryRegion memoryRegion                = (MemoryRegion)instruction[MemoryRegionIndex];
            Register     destinationRegister         = context.GetRegister(instruction[DestinationRegisterIndex]);
            byte         useDestinationAsSourceIndex = instruction[UseDestinationAsSourceIndex];
            ulong        address = InstructionHelper.GetImmediate(instruction, OffsetImmediateIndex, OffsetImmediateSize);

            Pointer sourceMemory;

            switch (useDestinationAsSourceIndex)
            {
            case 0:
                // Don't use the source register as an additional address offset.
                sourceMemory = MemoryHelper.EmitPointer(memoryRegion, address, context);
                break;

            case 1:
                // Use the source register as the base address.
                sourceMemory = MemoryHelper.EmitPointer(destinationRegister, address, context);
                break;

            default:
                throw new TamperCompilationException($"Invalid source mode {useDestinationAsSourceIndex} in Atmosphere cheat");
            }

            InstructionHelper.EmitMov(operationWidth, context, destinationRegister, sourceMemory);
        }
예제 #9
0
        public static ICondition Emit(byte[] instruction, CompilationContext context)
        {
            // 1TMC00AA AAAAAAAA VVVVVVVV (VVVVVVVV)
            // T: Width of memory write (1, 2, 4, or 8 bytes).
            // M: Memory region to write to (0 = Main NSO, 1 = Heap).
            // C: Condition to use, see below.
            // A: Immediate offset to use from memory region base.
            // V: Value to compare to.

            byte         operationWidth = instruction[OperationWidthIndex];
            MemoryRegion memoryRegion   = (MemoryRegion)instruction[MemoryRegionIndex];
            Comparison   comparison     = (Comparison)instruction[ComparisonTypeIndex];

            ulong   address      = InstructionHelper.GetImmediate(instruction, OffsetImmediateIndex, OffsetImmediateSize);
            Pointer sourceMemory = MemoryHelper.EmitPointer(memoryRegion, address, context);

            int           valueSize      = operationWidth <= 4 ? ValueImmediateSize4 : ValueImmediateSize8;
            ulong         value          = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, valueSize);
            Value <ulong> compareToValue = new Value <ulong>(value);

            return(InstructionHelper.CreateCondition(comparison, operationWidth, sourceMemory, compareToValue));
        }
예제 #10
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // 0TMR00AA AAAAAAAA VVVVVVVV (VVVVVVVV)
            // T: Width of memory write(1, 2, 4, or 8 bytes).
            // M: Memory region to write to(0 = Main NSO, 1 = Heap).
            // R: Register to use as an offset from memory region base.
            // A: Immediate offset to use from memory region base.
            // V: Value to write.

            byte         operationWidth  = instruction[OperationWidthIndex];
            MemoryRegion memoryRegion    = (MemoryRegion)instruction[MemoryRegionIndex];
            Register     offsetRegister  = context.GetRegister(instruction[OffsetRegisterIndex]);
            ulong        offsetImmediate = InstructionHelper.GetImmediate(instruction, OffsetImmediateIndex, OffsetImmediateSize);

            Pointer dstMem = MemoryHelper.EmitPointer(memoryRegion, offsetRegister, offsetImmediate, context);

            int           valueImmediateSize = operationWidth <= 4 ? ValueImmediateSize8 : ValueImmediateSize16;
            ulong         valueImmediate     = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, valueImmediateSize);
            Value <ulong> storeValue         = new Value <ulong>(valueImmediate);

            InstructionHelper.EmitMov(operationWidth, context, dstMem, storeValue);
        }
예제 #11
0
        public static ICondition Emit(byte[] instruction, CompilationContext context)
        {
            // C0TcSX##
            // C0TcS0Ma aaaaaaaa
            // C0TcS1Mr
            // C0TcS2Ra aaaaaaaa
            // C0TcS3Rr
            // C0TcS400 VVVVVVVV (VVVVVVVV)
            // C0TcS5X0
            // T: Width of memory write(1, 2, 4, or 8 bytes).
            // c: Condition to use, see below.
            // S: Source Register.
            // X: Operand Type, see below.
            // M: Memory Type(operand types 0 and 1).
            // R: Address Register(operand types 2 and 3).
            // a: Relative Address(operand types 0 and 2).
            // r: Offset Register(operand types 1 and 3).
            // X: Other Register(operand type 5).
            // V: Value to compare to(operand type 4).

            byte       operationWidth         = instruction[OperationWidthIndex];
            Comparison comparison             = (Comparison)instruction[ComparisonTypeIndex];
            Register   sourceRegister         = context.GetRegister(instruction[SourceRegisterIndex]);
            byte       operandType            = instruction[OperandTypeIndex];
            byte       registerOrMemoryRegion = instruction[RegisterOrMemoryRegionIndex];
            byte       offsetRegisterIndex    = instruction[OffsetImmediateIndex];
            ulong      offsetImmediate;
            ulong      valueImmediate;
            int        valueImmediateSize;
            Register   addressRegister;
            Register   offsetRegister;
            IOperand   sourceOperand;

            switch (operandType)
            {
            case MemoryRegionWithOffsetImmediate:
                // *(?x + #a)
                offsetImmediate = InstructionHelper.GetImmediate(instruction, OffsetImmediateIndex, OffsetImmediateSize);
                sourceOperand   = MemoryHelper.EmitPointer((MemoryRegion)registerOrMemoryRegion, offsetImmediate, context);
                break;

            case MemoryRegionWithOffsetRegister:
                // *(?x + $r)
                offsetRegister = context.GetRegister(offsetRegisterIndex);
                sourceOperand  = MemoryHelper.EmitPointer((MemoryRegion)registerOrMemoryRegion, offsetRegister, context);
                break;

            case AddressRegisterWithOffsetImmediate:
                // *($R + #a)
                addressRegister = context.GetRegister(registerOrMemoryRegion);
                offsetImmediate = InstructionHelper.GetImmediate(instruction, OffsetImmediateIndex, OffsetImmediateSize);
                sourceOperand   = MemoryHelper.EmitPointer(addressRegister, offsetImmediate, context);
                break;

            case AddressRegisterWithOffsetRegister:
                // *($R + $r)
                addressRegister = context.GetRegister(registerOrMemoryRegion);
                offsetRegister  = context.GetRegister(offsetRegisterIndex);
                sourceOperand   = MemoryHelper.EmitPointer(addressRegister, offsetRegister, context);
                break;

            case OffsetImmediate:
                valueImmediateSize = operationWidth <= 4 ? ValueImmediateSize8 : ValueImmediateSize16;
                valueImmediate     = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, valueImmediateSize);
                sourceOperand      = new Value <ulong>(valueImmediate);
                break;

            case AddressRegister:
                // $V
                sourceOperand = context.GetRegister(registerOrMemoryRegion);
                break;

            default:
                throw new TamperCompilationException($"Invalid operand type {operandType} in Atmosphere cheat");
            }

            return(InstructionHelper.CreateCondition(comparison, operationWidth, sourceRegister, sourceOperand));
        }
예제 #12
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // FFFTIX##
            // FFFTI0Ma aaaaaaaa
            // FFFTI1Mr
            // FFFTI2Ra aaaaaaaa
            // FFFTI3Rr
            // FFFTI4V0
            // T: Width of memory write (1, 2, 4, or 8 bytes).
            // I: Log id.
            // X: Operand Type, see below.
            // M: Memory Type (operand types 0 and 1).
            // R: Address Register (operand types 2 and 3).
            // a: Relative Address (operand types 0 and 2).
            // r: Offset Register (operand types 1 and 3).
            // V: Value Register (operand type 4).

            byte     operationWidth         = instruction[OperationWidthIndex];
            byte     logId                  = instruction[LogIdIndex];
            byte     operandType            = instruction[OperandTypeIndex];
            byte     registerOrMemoryRegion = instruction[RegisterOrMemoryRegionIndex];
            byte     offsetRegisterIndex    = instruction[OffsetRegisterOrImmediateIndex];
            ulong    immediate;
            Register addressRegister;
            Register offsetRegister;
            IOperand sourceOperand;

            switch (operandType)
            {
            case MemoryRegionWithOffsetImmediate:
                // *(?x + #a)
                immediate     = InstructionHelper.GetImmediate(instruction, OffsetRegisterOrImmediateIndex, OffsetImmediateSize);
                sourceOperand = MemoryHelper.EmitPointer((MemoryRegion)registerOrMemoryRegion, immediate, context);
                break;

            case MemoryRegionWithOffsetRegister:
                // *(?x + $r)
                offsetRegister = context.GetRegister(offsetRegisterIndex);
                sourceOperand  = MemoryHelper.EmitPointer((MemoryRegion)registerOrMemoryRegion, offsetRegister, context);
                break;

            case AddressRegisterWithOffsetImmediate:
                // *($R + #a)
                addressRegister = context.GetRegister(registerOrMemoryRegion);
                immediate       = InstructionHelper.GetImmediate(instruction, OffsetRegisterOrImmediateIndex, OffsetImmediateSize);
                sourceOperand   = MemoryHelper.EmitPointer(addressRegister, immediate, context);
                break;

            case AddressRegisterWithOffsetRegister:
                // *($R + $r)
                addressRegister = context.GetRegister(registerOrMemoryRegion);
                offsetRegister  = context.GetRegister(offsetRegisterIndex);
                sourceOperand   = MemoryHelper.EmitPointer(addressRegister, offsetRegister, context);
                break;

            case ValueRegister:
                // $V
                sourceOperand = context.GetRegister(registerOrMemoryRegion);
                break;

            default:
                throw new TamperCompilationException($"Invalid operand type {operandType} in Atmosphere cheat");
            }

            InstructionHelper.Emit(typeof(OpLog <>), operationWidth, context, logId, sourceOperand);
        }
예제 #13
0
        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // ATSRIOxa (aaaaaaaa)
            // T: Width of memory write (1, 2, 4, or 8 bytes).
            // S: Register to write to memory.
            // R: Register to use as base address.
            // I: Increment register flag (0 = do not increment R, 1 = increment R by T).
            // O: Offset type, see below.
            // x: Register used as offset when O is 1, Memory type when O is 3, 4 or 5.
            // a: Value used as offset when O is 2, 4 or 5.

            byte     operationWidth           = instruction[OperationWidthIndex];
            Register sourceRegister           = context.GetRegister(instruction[SourceRegisterIndex]);
            Register addressRegister          = context.GetRegister(instruction[AddressRegisterIndex]);
            byte     incrementAddressRegister = instruction[IncrementAddressRegisterIndex];
            byte     offsetType             = instruction[AddressingTypeIndex];
            byte     registerOrMemoryRegion = instruction[RegisterOrMemoryRegionIndex];
            int      immediateSize          = instruction.Length <= 8 ? OffsetImmediateSize1 : OffsetImmediateSize9;
            ulong    immediate = InstructionHelper.GetImmediate(instruction, OffsetImmediateIndex, immediateSize);

            Pointer destinationMemory;

            switch (offsetType)
            {
            case AddressRegister:
                // *($R) = $S
                destinationMemory = MemoryHelper.EmitPointer(addressRegister, context);
                break;

            case AddressRegisterWithOffsetRegister:
                // *($R + $x) = $S
                Register offsetRegister = context.GetRegister(registerOrMemoryRegion);
                destinationMemory = MemoryHelper.EmitPointer(addressRegister, offsetRegister, context);
                break;

            case OffsetImmediate:
                // *(#a) = $S
                destinationMemory = MemoryHelper.EmitPointer(addressRegister, immediate, context);
                break;

            case MemoryRegionWithOffsetRegister:
                // *(?x + $R) = $S
                destinationMemory = MemoryHelper.EmitPointer((MemoryRegion)registerOrMemoryRegion, addressRegister, context);
                break;

            case MemoryRegionWithOffsetImmediate:
                // *(?x + #a) = $S
                destinationMemory = MemoryHelper.EmitPointer((MemoryRegion)registerOrMemoryRegion, immediate, context);
                break;

            case MemoryRegionWithOffsetRegisterAndImmediate:
                // *(?x + #a + $R) = $S
                destinationMemory = MemoryHelper.EmitPointer((MemoryRegion)registerOrMemoryRegion, addressRegister, immediate, context);
                break;

            default:
                throw new TamperCompilationException($"Invalid offset type {offsetType} in Atmosphere cheat");
            }

            InstructionHelper.EmitMov(operationWidth, context, destinationMemory, sourceRegister);

            switch (incrementAddressRegister)
            {
            case 0:
                // Don't increment the address register by operationWidth.
                break;

            case 1:
                // Increment the address register by operationWidth.
                IOperand increment = new Value <ulong>(operationWidth);
                context.CurrentOperations.Add(new OpAdd <ulong>(addressRegister, addressRegister, increment));
                break;

            default:
                throw new TamperCompilationException($"Invalid increment mode {incrementAddressRegister} in Atmosphere cheat");
            }
        }
예제 #14
0
        private const byte Mov = 9; // lhs (discards right-hand operand)

        public static void Emit(byte[] instruction, CompilationContext context)
        {
            // 9TCRS0s0
            // T: Width of arithmetic operation(1, 2, 4, or 8 bytes).
            // C: Arithmetic operation to apply, see below.
            // R: Register to store result in.
            // S: Register to use as left - hand operand.
            // s: Register to use as right - hand operand.

            // 9TCRS100 VVVVVVVV (VVVVVVVV)
            // T: Width of arithmetic operation(1, 2, 4, or 8 bytes).
            // C: Arithmetic operation to apply, see below.
            // R: Register to store result in.
            // S: Register to use as left - hand operand.
            // V: Value to use as right - hand operand.

            byte     operationWidth           = instruction[OperationWidthIndex];
            byte     operation                = instruction[OperationTypeIndex];
            Register destinationRegister      = context.GetRegister(instruction[DestinationRegisterIndex]);
            Register leftHandSideRegister     = context.GetRegister(instruction[LeftHandSideRegisterIndex]);
            byte     rightHandSideIsImmediate = instruction[UseImmediateAsRhsIndex];
            IOperand rightHandSideOperand;

            switch (rightHandSideIsImmediate)
            {
            case 0:
                // Use a register as right-hand side.
                rightHandSideOperand = context.GetRegister(instruction[RightHandSideRegisterIndex]);
                break;

            case 1:
                // Use an immediate as right-hand side.
                int   immediateSize = operationWidth <= 4 ? RightHandSideImmediate8 : RightHandSideImmediate16;
                ulong immediate     = InstructionHelper.GetImmediate(instruction, RightHandSideImmediateIndex, immediateSize);
                rightHandSideOperand = new Value <ulong>(immediate);
                break;

            default:
                throw new TamperCompilationException($"Invalid right-hand side switch {rightHandSideIsImmediate} in Atmosphere cheat");
            }

            void Emit(Type operationType, IOperand rhs = null)
            {
                List <IOperand> operandList = new List <IOperand>();

                operandList.Add(destinationRegister);
                operandList.Add(leftHandSideRegister);

                if (rhs != null)
                {
                    operandList.Add(rhs);
                }

                InstructionHelper.Emit(operationType, operationWidth, context, operandList.ToArray());
            }

            switch (operation)
            {
            case Add: Emit(typeof(OpAdd <>), rightHandSideOperand); break;

            case Sub: Emit(typeof(OpSub <>), rightHandSideOperand); break;

            case Mul: Emit(typeof(OpMul <>), rightHandSideOperand); break;

            case Lsh: Emit(typeof(OpLsh <>), rightHandSideOperand); break;

            case Rsh: Emit(typeof(OpRsh <>), rightHandSideOperand); break;

            case And: Emit(typeof(OpAnd <>), rightHandSideOperand); break;

            case Or:  Emit(typeof(OpOr <>), rightHandSideOperand); break;

            case Not: Emit(typeof(OpNot <>)); break;

            case Xor: Emit(typeof(OpXor <>), rightHandSideOperand); break;

            case Mov: Emit(typeof(OpMov <>)); break;

            default:
                throw new TamperCompilationException($"Invalid arithmetic operation {operation} in Atmosphere cheat");
            }
        }