Remove dynamic usage from Ryujinx.HLE (will come back to make this not generic explode)

This commit is contained in:
Aaron Robinson 2025-11-19 00:33:17 -06:00
parent 4f5eea3417
commit 00021121ea
31 changed files with 198 additions and 106 deletions

View file

@ -1,7 +1,5 @@
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Tamper.Operations;
using System;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
{
@ -71,53 +69,42 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
throw new TamperCompilationException($"Invalid right-hand side switch {rightHandSideIsImmediate} in Atmosphere cheat");
}
void Emit(Type operationType, IOperand rhs = null)
void EmitCore<TOp>(IOperand rhs = null) where TOp : IOperation
{
List<IOperand> operandList =
[
destinationRegister,
leftHandSideRegister
];
if (rhs != null)
{
operandList.Add(rhs);
}
InstructionHelper.Emit(operationType, operationWidth, context, operandList.ToArray());
InstructionHelper.Emit<TOp>(operationWidth, context, destinationRegister, leftHandSideRegister, rhs);
}
switch (operation)
{
case Add:
Emit(typeof(OpAdd<>), rightHandSideOperand);
EmitCore<OpAdd<byte>>(rightHandSideOperand);
break;
case Sub:
Emit(typeof(OpSub<>), rightHandSideOperand);
EmitCore<OpSub<byte>>(rightHandSideOperand);
break;
case Mul:
Emit(typeof(OpMul<>), rightHandSideOperand);
EmitCore<OpMul<byte>>(rightHandSideOperand);
break;
case Lsh:
Emit(typeof(OpLsh<>), rightHandSideOperand);
EmitCore<OpLsh<byte>>(rightHandSideOperand);
break;
case Rsh:
Emit(typeof(OpRsh<>), rightHandSideOperand);
EmitCore<OpRsh<byte>>(rightHandSideOperand);
break;
case And:
Emit(typeof(OpAnd<>), rightHandSideOperand);
EmitCore<OpAnd<byte>>(rightHandSideOperand);
break;
case Or:
Emit(typeof(OpOr<>), rightHandSideOperand);
EmitCore<OpOr<byte>>(rightHandSideOperand);
break;
case Not:
Emit(typeof(OpNot<>));
EmitCore<OpNot<byte>>();
break;
case Xor:
Emit(typeof(OpXor<>), rightHandSideOperand);
EmitCore<OpXor<byte>>(rightHandSideOperand);
break;
case Mov:
Emit(typeof(OpMov<>));
EmitCore<OpMov<byte>>();
break;
default:
throw new TamperCompilationException($"Invalid arithmetic operation {operation} in Atmosphere cheat");

View file

@ -1,5 +1,6 @@
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Tamper.Operations;
using System;
namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
{
@ -81,7 +82,15 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
throw new TamperCompilationException($"Invalid operand type {operandType} in Atmosphere cheat");
}
InstructionHelper.Emit(typeof(OpLog<>), operationWidth, context, logId, sourceOperand);
IOperation op = operationWidth switch
{
1 => new OpLog<byte>(logId, sourceOperand),
2 => new OpLog<ushort>(logId, sourceOperand),
4 => new OpLog<uint>(logId, sourceOperand),
8 => new OpLog<ulong>(logId, sourceOperand),
_ => throw new NotSupportedException(),
};
InstructionHelper.Emit(op, context);
}
}
}

View file

@ -37,27 +37,27 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
ulong immediate = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, ValueImmediateSize);
Value<ulong> rightHandSideValue = new(immediate);
void Emit(Type operationType)
void EmitCore<TOp>() where TOp : IOperation
{
InstructionHelper.Emit(operationType, operationWidth, context, register, register, rightHandSideValue);
InstructionHelper.Emit<TOp>(operationWidth, context, register, register, rightHandSideValue);
}
switch (operation)
{
case Add:
Emit(typeof(OpAdd<>));
EmitCore<OpAdd<byte>>();
break;
case Sub:
Emit(typeof(OpSub<>));
EmitCore<OpAdd<byte>>();
break;
case Mul:
Emit(typeof(OpMul<>));
EmitCore<OpMul<byte>>();
break;
case Lsh:
Emit(typeof(OpLsh<>));
EmitCore<OpLsh<byte>>();
break;
case Rsh:
Emit(typeof(OpRsh<>));
EmitCore<OpRsh<byte>>();
break;
default:
throw new TamperCompilationException($"Invalid arithmetic operation {operation} in Atmosphere cheat");

View file

@ -1,4 +1,5 @@
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Tamper.Operations;
namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
{
@ -52,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
throw new TamperCompilationException($"Invalid source mode {useDestinationAsSourceIndex} in Atmosphere cheat");
}
InstructionHelper.EmitMov(operationWidth, context, destinationRegister, sourceMemory);
InstructionHelper.Emit<OpMov<byte>>(operationWidth, context, destinationRegister, sourceMemory, null);
}
}
}

View file

@ -1,3 +1,5 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
{
/// <summary>
@ -35,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
ulong valueImmediate = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, valueImmediateSize);
Value<ulong> storeValue = new(valueImmediate);
InstructionHelper.EmitMov(operationWidth, context, dstMem, storeValue);
InstructionHelper.Emit<OpMov<byte>>(operationWidth, context, dstMem, storeValue, null);
}
}
}

View file

@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
throw new TamperCompilationException($"Invalid offset mode {useOffsetRegister} in Atmosphere cheat");
}
InstructionHelper.EmitMov(operationWidth, context, destinationMemory, storeValue);
InstructionHelper.Emit<OpMov<byte>>(operationWidth, context, destinationMemory, storeValue, null);
switch (incrementAddressRegister)
{

View file

@ -79,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
throw new TamperCompilationException($"Invalid offset type {offsetType} in Atmosphere cheat");
}
InstructionHelper.EmitMov(operationWidth, context, destinationMemory, sourceRegister);
InstructionHelper.Emit<OpMov<byte>>(operationWidth, context, destinationMemory, sourceRegister, null);
switch (incrementAddressRegister)
{

View file

@ -1,8 +1,9 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Conditions
{
class CondEQ<T> : ICondition where T : unmanaged
class CondEQ<T> : ICondition where T : unmanaged, INumber<T>
{
private readonly IOperand _lhs;
private readonly IOperand _rhs;
@ -15,7 +16,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
public bool Evaluate()
{
return (dynamic)_lhs.Get<T>() == (dynamic)_rhs.Get<T>();
return _lhs.Get<T>() == _rhs.Get<T>();
}
public static ICondition CreateFor<T1>(IOperand lhs, IOperand rhs) where T1 : INumber<T1>
=> new CondEQ<T>(lhs, rhs);
}
}

View file

@ -1,8 +1,9 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Conditions
{
class CondGE<T> : ICondition where T : unmanaged
class CondGE<T> : ICondition where T : unmanaged, INumber<T>
{
private readonly IOperand _lhs;
private readonly IOperand _rhs;
@ -15,7 +16,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
public bool Evaluate()
{
return (dynamic)_lhs.Get<T>() >= (dynamic)_rhs.Get<T>();
return _lhs.Get<T>() >= _rhs.Get<T>();
}
public static ICondition CreateFor<T1>(IOperand lhs, IOperand rhs) where T1 : INumber<T1>
=> new CondGE<T>(lhs, rhs);
}
}

View file

@ -1,8 +1,9 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Conditions
{
class CondGT<T> : ICondition where T : unmanaged
class CondGT<T> : ICondition where T : unmanaged, INumber<T>
{
private readonly IOperand _lhs;
private readonly IOperand _rhs;
@ -15,7 +16,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
public bool Evaluate()
{
return (dynamic)_lhs.Get<T>() > (dynamic)_rhs.Get<T>();
return _lhs.Get<T>() > _rhs.Get<T>();
}
public static ICondition CreateFor<T1>(IOperand lhs, IOperand rhs) where T1 : INumber<T1>
=> new CondGT<T>(lhs, rhs);
}
}

View file

@ -1,8 +1,9 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Conditions
{
class CondLE<T> : ICondition where T : unmanaged
class CondLE<T> : ICondition where T : unmanaged, INumber<T>
{
private readonly IOperand _lhs;
private readonly IOperand _rhs;
@ -15,7 +16,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
public bool Evaluate()
{
return (dynamic)_lhs.Get<T>() <= (dynamic)_rhs.Get<T>();
return _lhs.Get<T>() <= _rhs.Get<T>();
}
public static ICondition CreateFor<T1>(IOperand lhs, IOperand rhs) where T1 : INumber<T1>
=> new CondLE<T>(lhs, rhs);
}
}

View file

@ -1,8 +1,9 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Conditions
{
class CondLT<T> : ICondition where T : unmanaged
class CondLT<T> : ICondition where T : unmanaged, INumber<T>
{
private readonly IOperand _lhs;
private readonly IOperand _rhs;
@ -15,7 +16,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
public bool Evaluate()
{
return (dynamic)_lhs.Get<T>() < (dynamic)_rhs.Get<T>();
return _lhs.Get<T>() < _rhs.Get<T>();
}
public static ICondition CreateFor<T1>(IOperand lhs, IOperand rhs) where T1 : INumber<T1>
=> new CondLT<T>(lhs, rhs);
}
}

View file

@ -1,8 +1,9 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Conditions
{
class CondNE<T> : ICondition where T : unmanaged
class CondNE<T> : ICondition where T : unmanaged, INumber<T>
{
private readonly IOperand _lhs;
private readonly IOperand _rhs;
@ -15,7 +16,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
public bool Evaluate()
{
return (dynamic)_lhs.Get<T>() != (dynamic)_rhs.Get<T>();
return _lhs.Get<T>() != _rhs.Get<T>();
}
public static ICondition CreateFor<T1>(IOperand lhs, IOperand rhs) where T1 : INumber<T1>
=> new CondNE<T>(lhs, rhs);
}
}

View file

@ -1,7 +1,13 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
using System;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Conditions
{
interface ICondition
{
bool Evaluate();
static virtual ICondition CreateFor<T>(IOperand lhs, IOperand rhs) where T : INumber<T> => throw new NotImplementedException();
}
}

View file

@ -15,46 +15,47 @@ namespace Ryujinx.HLE.HOS.Tamper
context.CurrentOperations.Add(operation);
}
public static void Emit(Type instruction, byte width, CompilationContext context, params Object[] operands)
public static void Emit<TOp>(byte width, CompilationContext context, IOperand destination, IOperand lhs, IOperand rhs) where TOp : IOperation
{
Emit((IOperation)Create(instruction, width, operands), context);
}
public static void EmitMov(byte width, CompilationContext context, IOperand destination, IOperand source)
{
Emit(typeof(OpMov<>), width, context, destination, source);
Emit(Create<TOp>(width, destination, lhs, rhs), context);
}
public static ICondition CreateCondition(Comparison comparison, byte width, IOperand lhs, IOperand rhs)
{
ICondition Create(Type conditionType)
ICondition CreateCore<TOp>() where TOp : ICondition
{
return (ICondition)InstructionHelper.Create(conditionType, width, lhs, rhs);
return width switch
{
1 => TOp.CreateFor<byte>(lhs, rhs),
2 => TOp.CreateFor<ushort>(lhs, rhs),
4 => TOp.CreateFor<uint>(lhs, rhs),
8 => TOp.CreateFor<ulong>(lhs, rhs),
_ => throw new NotSupportedException(),
};
}
return comparison switch
{
Comparison.Greater => Create(typeof(CondGT<>)),
Comparison.GreaterOrEqual => Create(typeof(CondGE<>)),
Comparison.Less => Create(typeof(CondLT<>)),
Comparison.LessOrEqual => Create(typeof(CondLE<>)),
Comparison.Equal => Create(typeof(CondEQ<>)),
Comparison.NotEqual => Create(typeof(CondNE<>)),
Comparison.Greater => CreateCore<CondGT<byte>>(),
Comparison.GreaterOrEqual => CreateCore<CondGE<byte>>(),
Comparison.Less => CreateCore<CondLT<byte>>(),
Comparison.LessOrEqual => CreateCore<CondLE<byte>>(),
Comparison.Equal => CreateCore<CondEQ<byte>>(),
Comparison.NotEqual => CreateCore<CondNE<byte>>(),
_ => throw new TamperCompilationException($"Invalid comparison {comparison} in Atmosphere cheat"),
};
}
public static Object Create(Type instruction, byte width, params Object[] operands)
public static IOperation Create<TOp>(byte width, IOperand destination, IOperand lhs, IOperand rhs) where TOp : IOperation
{
Type realType = width switch
return width switch
{
1 => instruction.MakeGenericType(typeof(byte)),
2 => instruction.MakeGenericType(typeof(ushort)),
4 => instruction.MakeGenericType(typeof(uint)),
8 => instruction.MakeGenericType(typeof(ulong)),
_ => throw new TamperCompilationException($"Invalid instruction width {width} in Atmosphere cheat"),
1 => TOp.CreateFor<byte>(destination, lhs, rhs),
2 => TOp.CreateFor<ushort>(destination, lhs, rhs),
4 => TOp.CreateFor<uint>(destination, lhs, rhs),
8 => TOp.CreateFor<ulong>(destination, lhs, rhs),
_ => throw new NotSupportedException(),
};
return Activator.CreateInstance(realType, operands);
}
public static ulong GetImmediate(byte[] instruction, int index, int nybbleCount)

View file

@ -1,8 +1,10 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
interface IOperand
{
public T Get<T>() where T : unmanaged;
public void Set<T>(T value) where T : unmanaged;
public T Get<T>() where T : unmanaged, INumber<T>;
public void Set<T>(T value) where T : unmanaged, INumber<T>;
}
}

View file

@ -1,7 +1,13 @@
using System;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
interface IOperation
{
void Execute();
static virtual IOperation CreateFor<T>(IOperand destination, IOperand lhs, IOperand rhs) where T : unmanaged, IBinaryInteger<T>
=> throw new NotImplementedException();
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpAdd<T> : IOperation where T : unmanaged
class OpAdd<T> : IOperation where T : unmanaged, INumber<T>
{
readonly IOperand _destination;
readonly IOperand _lhs;
@ -15,7 +17,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)((dynamic)_lhs.Get<T>() + (dynamic)_rhs.Get<T>()));
_destination.Set(_lhs.Get<T>() + _rhs.Get<T>());
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpAdd<T1>(destination, lhs, rhs);
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpAnd<T> : IOperation where T : unmanaged
class OpAnd<T> : IOperation where T : unmanaged, IBinaryNumber<T>
{
readonly IOperand _destination;
readonly IOperand _lhs;
@ -15,7 +17,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)((dynamic)_lhs.Get<T>() & (dynamic)_rhs.Get<T>()));
_destination.Set(_lhs.Get<T>() & _rhs.Get<T>());
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpAnd<T1>(destination, lhs, rhs);
}
}

View file

@ -1,8 +1,9 @@
using Ryujinx.Common.Logging;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpLog<T> : IOperation where T : unmanaged
class OpLog<T> : IOperation where T : unmanaged, INumber<T>
{
readonly int _logId;
readonly IOperand _source;
@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
Logger.Debug?.Print(LogClass.TamperMachine, $"Tamper debug log id={_logId} value={(dynamic)_source.Get<T>():X}");
Logger.Debug?.Print(LogClass.TamperMachine, $"Tamper debug log id={_logId} value={_source.Get<T>():X}");
}
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpLsh<T> : IOperation where T : unmanaged
class OpLsh<T> : IOperation where T : unmanaged, IBinaryInteger<T>
{
readonly IOperand _destination;
readonly IOperand _lhs;
@ -15,7 +17,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)((dynamic)_lhs.Get<T>() << (dynamic)_rhs.Get<T>()));
_destination.Set(_lhs.Get<T>() << int.CreateTruncating(_rhs.Get<T>()));
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpLsh<T1>(destination, lhs, rhs);
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpMov<T> : IOperation where T : unmanaged
class OpMov<T> : IOperation where T : unmanaged, INumber<T>
{
readonly IOperand _destination;
readonly IOperand _source;
@ -15,5 +17,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
{
_destination.Set(_source.Get<T>());
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpMov<T1>(destination, lhs);
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpMul<T> : IOperation where T : unmanaged
class OpMul<T> : IOperation where T : unmanaged, INumber<T>
{
readonly IOperand _destination;
readonly IOperand _lhs;
@ -15,7 +17,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)((dynamic)_lhs.Get<T>() * (dynamic)_rhs.Get<T>()));
_destination.Set(_lhs.Get<T>() * _rhs.Get<T>());
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpMul<T1>(destination, lhs, rhs);
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpNot<T> : IOperation where T : unmanaged
class OpNot<T> : IOperation where T : unmanaged, IBinaryNumber<T>
{
readonly IOperand _destination;
readonly IOperand _source;
@ -13,7 +15,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)(~(dynamic)_source.Get<T>()));
_destination.Set(~_source.Get<T>());
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpNot<T1>(destination, lhs);
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpOr<T> : IOperation where T : unmanaged
class OpOr<T> : IOperation where T : unmanaged, IBinaryNumber<T>
{
readonly IOperand _destination;
readonly IOperand _lhs;
@ -15,7 +17,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)((dynamic)_lhs.Get<T>() | (dynamic)_rhs.Get<T>()));
_destination.Set(_lhs.Get<T>() | _rhs.Get<T>());
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpOr<T1>(destination, lhs, rhs);
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpRsh<T> : IOperation where T : unmanaged
class OpRsh<T> : IOperation where T : unmanaged, IBinaryInteger<T>
{
readonly IOperand _destination;
readonly IOperand _lhs;
@ -15,7 +17,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)((dynamic)_lhs.Get<T>() >> (dynamic)_rhs.Get<T>()));
_destination.Set(_lhs.Get<T>() >> int.CreateTruncating(_rhs.Get<T>()));
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpRsh<T1>(destination, lhs, rhs);
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpSub<T> : IOperation where T : unmanaged
class OpSub<T> : IOperation where T : unmanaged, INumber<T>
{
readonly IOperand _destination;
readonly IOperand _lhs;
@ -15,7 +17,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)((dynamic)_lhs.Get<T>() - (dynamic)_rhs.Get<T>()));
_destination.Set(_lhs.Get<T>() - _rhs.Get<T>());
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpSub<T1>(destination, lhs, rhs);
}
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper.Operations
{
class OpXor<T> : IOperation where T : unmanaged
class OpXor<T> : IOperation where T : unmanaged, IBinaryNumber<T>
{
readonly IOperand _destination;
readonly IOperand _lhs;
@ -15,7 +17,10 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations
public void Execute()
{
_destination.Set((T)((dynamic)_lhs.Get<T>() ^ (dynamic)_rhs.Get<T>()));
_destination.Set(_lhs.Get<T>() ^ _rhs.Get<T>());
}
public static IOperation CreateFor<T1>(IOperand destination, IOperand lhs, IOperand rhs) where T1 : unmanaged, IBinaryInteger<T1>
=> new OpXor<T1>(destination, lhs, rhs);
}
}

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Ryujinx.HLE.HOS.Tamper
@ -15,12 +16,12 @@ namespace Ryujinx.HLE.HOS.Tamper
_process = process;
}
public T Get<T>() where T : unmanaged
public T Get<T>() where T : unmanaged, INumber<T>
{
return _process.ReadMemory<T>(_position.Get<ulong>());
}
public void Set<T>(T value) where T : unmanaged
public void Set<T>(T value) where T : unmanaged, INumber<T>
{
ulong position = _position.Get<ulong>();

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper
{
@ -13,16 +14,16 @@ namespace Ryujinx.HLE.HOS.Tamper
_alias = alias;
}
public T Get<T>() where T : unmanaged
public T Get<T>() where T : unmanaged, INumber<T>
{
return (T)(dynamic)_register;
return T.CreateTruncating(_register);
}
public void Set<T>(T value) where T : unmanaged
public void Set<T>(T value) where T : unmanaged, INumber<T>
{
Logger.Debug?.Print(LogClass.TamperMachine, $"{_alias}: {value}");
_register = (ulong)(dynamic)value;
_register = ulong.CreateTruncating(value);
}
}
}

View file

@ -1,8 +1,9 @@
using Ryujinx.HLE.HOS.Tamper.Operations;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Tamper
{
class Value<TP> : IOperand where TP : unmanaged
class Value<TP> : IOperand where TP : unmanaged, INumber<TP>
{
private TP _value;
@ -11,14 +12,14 @@ namespace Ryujinx.HLE.HOS.Tamper
_value = value;
}
public T Get<T>() where T : unmanaged
public T Get<T>() where T : unmanaged, INumber<T>
{
return (T)(dynamic)_value;
return T.CreateTruncating(_value);
}
public void Set<T>(T value) where T : unmanaged
public void Set<T>(T value) where T : unmanaged, INumber<T>
{
_value = (TP)(dynamic)value;
_value = TP.CreateTruncating(value);
}
}
}