From 43feb68b11c5c5719801dce686b5db5d6ccfc2f2 Mon Sep 17 00:00:00 2001 From: merry Date: Thu, 10 Feb 2022 20:57:29 +0000 Subject: [PATCH] T16: Implement ANDS, EORS, LSLS, LSRS, ASRS, ADCS, SBCS, RORS, TST, NEGS, CMP, CMN, ORRS, MULS, BICS, MVNS (low registers) --- ARMeilleure/Decoders/IOpCode32AluRsReg.cs | 10 ++ ARMeilleure/Decoders/OpCode32AluRsReg.cs | 2 +- ARMeilleure/Decoders/OpCodeT16AluImmZero.cs | 26 ++++++ ARMeilleure/Decoders/OpCodeT16AluRegLow.cs | 22 +++++ ARMeilleure/Decoders/OpCodeT16ShiftReg.cs | 29 ++++++ ARMeilleure/Decoders/OpCodeTable.cs | 16 ++++ ARMeilleure/Instructions/InstEmitAluHelper.cs | 4 +- Ryujinx.Tests/Cpu/CpuTestThumb.cs | 91 +++++++++++++++++++ 8 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 ARMeilleure/Decoders/IOpCode32AluRsReg.cs create mode 100644 ARMeilleure/Decoders/OpCodeT16AluImmZero.cs create mode 100644 ARMeilleure/Decoders/OpCodeT16AluRegLow.cs create mode 100644 ARMeilleure/Decoders/OpCodeT16ShiftReg.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluRsReg.cs b/ARMeilleure/Decoders/IOpCode32AluRsReg.cs new file mode 100644 index 000000000..879db0593 --- /dev/null +++ b/ARMeilleure/Decoders/IOpCode32AluRsReg.cs @@ -0,0 +1,10 @@ +namespace ARMeilleure.Decoders +{ + interface IOpCode32AluRsReg : IOpCode32Alu + { + int Rm { get; } + int Rs { get; } + + ShiftType ShiftType { get; } + } +} \ No newline at end of file diff --git a/ARMeilleure/Decoders/OpCode32AluRsReg.cs b/ARMeilleure/Decoders/OpCode32AluRsReg.cs index d1421f7dc..616987727 100644 --- a/ARMeilleure/Decoders/OpCode32AluRsReg.cs +++ b/ARMeilleure/Decoders/OpCode32AluRsReg.cs @@ -1,6 +1,6 @@ namespace ARMeilleure.Decoders { - class OpCode32AluRsReg : OpCode32Alu + class OpCode32AluRsReg : OpCode32Alu, IOpCode32AluRsReg { public int Rm { get; } public int Rs { get; } diff --git a/ARMeilleure/Decoders/OpCodeT16AluImmZero.cs b/ARMeilleure/Decoders/OpCodeT16AluImmZero.cs new file mode 100644 index 000000000..bbe5573d9 --- /dev/null +++ b/ARMeilleure/Decoders/OpCodeT16AluImmZero.cs @@ -0,0 +1,26 @@ +namespace ARMeilleure.Decoders +{ + class OpCodeT16AluImmZero : OpCodeT16, IOpCode32AluImm + { + public int Rd { get; } + public int Rn { get; } + + public bool SetFlags { get; } + + public int Immediate { get; } + + public bool IsRotated { get; } + + public static new OpCode Create(InstDescriptor inst, ulong address, int opCode, bool inITBlock) => new OpCodeT16AluImmZero(inst, address, opCode, inITBlock); + + public OpCodeT16AluImmZero(InstDescriptor inst, ulong address, int opCode, bool inITBlock) : base(inst, address, opCode, inITBlock) + { + Rd = (opCode >> 0) & 0x7; + Rn = (opCode >> 3) & 0x7; + Immediate = 0; + IsRotated = false; + + SetFlags = !inITBlock; + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Decoders/OpCodeT16AluRegLow.cs b/ARMeilleure/Decoders/OpCodeT16AluRegLow.cs new file mode 100644 index 000000000..d8fcb0c75 --- /dev/null +++ b/ARMeilleure/Decoders/OpCodeT16AluRegLow.cs @@ -0,0 +1,22 @@ +namespace ARMeilleure.Decoders +{ + class OpCodeT16AluRegLow : OpCodeT16, IOpCode32AluReg + { + public int Rm { get; } + public int Rd { get; } + public int Rn { get; } + + public bool SetFlags { get; } + + public static new OpCode Create(InstDescriptor inst, ulong address, int opCode, bool inITBlock) => new OpCodeT16AluRegLow(inst, address, opCode, inITBlock); + + public OpCodeT16AluRegLow(InstDescriptor inst, ulong address, int opCode, bool inITBlock) : base(inst, address, opCode, inITBlock) + { + Rd = (opCode >> 0) & 0x7; + Rn = (opCode >> 0) & 0x7; + Rm = (opCode >> 3) & 0x7; + + SetFlags = !inITBlock; + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Decoders/OpCodeT16ShiftReg.cs b/ARMeilleure/Decoders/OpCodeT16ShiftReg.cs new file mode 100644 index 000000000..1553ecb70 --- /dev/null +++ b/ARMeilleure/Decoders/OpCodeT16ShiftReg.cs @@ -0,0 +1,29 @@ +namespace ARMeilleure.Decoders +{ + class OpCodeT16ShiftReg : OpCodeT16, IOpCode32AluRsReg + { + public int Rm { get; } + public int Rs { get; } + public int Rd { get; } + + public int Rn { get; } + + public ShiftType ShiftType { get; } + + public bool SetFlags { get; } + + public new static OpCode Create(InstDescriptor inst, ulong address, int opCode, bool inITBlock) => new OpCodeT16ShiftReg(inst, address, opCode, inITBlock); + + public OpCodeT16ShiftReg(InstDescriptor inst, ulong address, int opCode, bool inITBlock) : base(inst, address, opCode, inITBlock) + { + Rd = (opCode >> 0) & 7; + Rm = (opCode >> 0) & 7; + Rn = (opCode >> 3) & 7; + Rs = (opCode >> 3) & 7; + + ShiftType = (ShiftType)(((opCode >> 6) & 1) | ((opCode >> 7) & 2)); + + SetFlags = !inITBlock; + } + } +} diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs index ab547d4dd..e4454c37b 100644 --- a/ARMeilleure/Decoders/OpCodeTable.cs +++ b/ARMeilleure/Decoders/OpCodeTable.cs @@ -983,6 +983,22 @@ namespace ARMeilleure.Decoders SetT16("00101xxxxxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT16AluImm8.Create); SetT16("00110xxxxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AluImm8.Create); SetT16("00111xxxxxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT16AluImm8.Create); + SetT16("0100000000xxxxxx", InstName.And, InstEmit32.And, OpCodeT16AluRegLow.Create); + SetT16("0100000001xxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT16AluRegLow.Create); + SetT16("0100000010xxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftReg.Create); + SetT16("0100000011xxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftReg.Create); + SetT16("0100000100xxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftReg.Create); + SetT16("0100000101xxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT16AluRegLow.Create); + SetT16("0100000110xxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT16AluRegLow.Create); + SetT16("0100000111xxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftReg.Create); + SetT16("0100001000xxxxxx", InstName.Tst, InstEmit32.Tst, OpCodeT16AluRegLow.Create); + SetT16("0100001001xxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT16AluImmZero.Create); + SetT16("0100001010xxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT16AluRegLow.Create); + SetT16("0100001011xxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCodeT16AluRegLow.Create); + SetT16("0100001100xxxxxx", InstName.Orr, InstEmit32.Orr, OpCodeT16AluRegLow.Create); + SetT16("0100001101xxxxxx", InstName.Mul, InstEmit32.Mul, OpCodeT16AluRegLow.Create); + SetT16("0100001110xxxxxx", InstName.Bic, InstEmit32.Bic, OpCodeT16AluRegLow.Create); + SetT16("0100001111xxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT16AluRegLow.Create); SetT16("010001110xxxx000", InstName.Bx, InstEmit32.Bx, OpCodeT16BReg.Create); #endregion diff --git a/ARMeilleure/Instructions/InstEmitAluHelper.cs b/ARMeilleure/Instructions/InstEmitAluHelper.cs index ac7b16187..9d5bd19e4 100644 --- a/ARMeilleure/Instructions/InstEmitAluHelper.cs +++ b/ARMeilleure/Instructions/InstEmitAluHelper.cs @@ -196,7 +196,7 @@ namespace ARMeilleure.Instructions case OpCode32AluImm16 op: return Const(op.Immediate); case IOpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry); - case OpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry); + case IOpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry); case IOpCode32AluReg op: return GetIntA32(context, op.Rm); @@ -303,7 +303,7 @@ namespace ARMeilleure.Instructions return shift; } - public static Operand GetMShiftedByReg(ArmEmitterContext context, OpCode32AluRsReg op, bool setCarry) + public static Operand GetMShiftedByReg(ArmEmitterContext context, IOpCode32AluRsReg op, bool setCarry) { Operand m = GetIntA32(context, op.Rm); Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs)); diff --git a/Ryujinx.Tests/Cpu/CpuTestThumb.cs b/Ryujinx.Tests/Cpu/CpuTestThumb.cs index 492bb0dbe..f3fb03409 100644 --- a/Ryujinx.Tests/Cpu/CpuTestThumb.cs +++ b/Ryujinx.Tests/Cpu/CpuTestThumb.cs @@ -9,6 +9,11 @@ namespace Ryujinx.Tests.Cpu { private const int RndCnt = 2; + public static uint RotateRight(uint value, int count) + { + return (value >> count) | (value << (32 - count)); + } + [Test, Pairwise] public void ShiftImm([Range(0u, 2u)] uint shiftType, [Range(1u, 0x1fu)] uint shiftImm, [Random(RndCnt)] uint w1, [Random(RndCnt)] uint w2) { @@ -116,5 +121,91 @@ namespace Ryujinx.Tests.Cpu goto cmpFlags; } } + + [Test, Pairwise] + public void AluRegLow([Range(0u, 0xfu)] uint op, [Random(RndCnt)] uint w1, [Random(RndCnt)] uint w2) + { + uint opcode = 0x4000; // ANDS , + + uint rd = 1; + uint rm = 2; + opcode |= ((rd & 7) << 0) | ((rm & 7) << 3) | ((op & 0xf) << 6); + + SingleThumbOpcode((ushort)opcode, r1: w1, r2: w2, runUnicorn: false); + + uint shift = w2 & 0xff; + switch (op) + { + case 0: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1 & w2)); + break; + case 1: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1 ^ w2)); + break; + case 2: + Assert.That(GetContext().GetX(1), Is.EqualTo(shift >= 32 ? 0 : (uint)(w1 << (int)shift))); + break; + case 3: + Assert.That(GetContext().GetX(1), Is.EqualTo(shift >= 32 ? 0 : (uint)(w1 >> (int)shift))); + break; + case 4: + Assert.That(GetContext().GetX(1), Is.EqualTo(shift >= 32 ? (uint)((int)w1 >> 31) : (uint)((int)w1 >> (int)shift))); + break; + case 5: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1 + w2)); + break; + case 6: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1 + ~w2)); + break; + case 7: + Assert.That(GetContext().GetX(1), Is.EqualTo(RotateRight(w1, (int)shift & 31))); + break; + case 8: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1)); + { + uint result = w1 & w2; + Assert.That(GetContext().GetPstateFlag(PState.NFlag), Is.EqualTo((result >> 31) != 0)); + Assert.That(GetContext().GetPstateFlag(PState.ZFlag), Is.EqualTo(result == 0)); + } + break; + case 9: + Assert.That(GetContext().GetX(1), Is.EqualTo((uint)-w2)); + break; + case 10: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1)); + { + uint result = w1 - w2; + uint overflow = (result ^ w1) & (w1 ^ w2); + Assert.That(GetContext().GetPstateFlag(PState.NFlag), Is.EqualTo((result >> 31) != 0)); + Assert.That(GetContext().GetPstateFlag(PState.ZFlag), Is.EqualTo(result == 0)); + Assert.That(GetContext().GetPstateFlag(PState.CFlag), Is.EqualTo(w1 >= w2)); + Assert.That(GetContext().GetPstateFlag(PState.VFlag), Is.EqualTo((overflow >> 31) != 0)); + } + break; + case 11: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1)); + { + uint result = w1 + w2; + uint overflow = (result ^ w1) & ~(w1 ^ w2); + Assert.That(GetContext().GetPstateFlag(PState.NFlag), Is.EqualTo((result >> 31) != 0)); + Assert.That(GetContext().GetPstateFlag(PState.ZFlag), Is.EqualTo(result == 0)); + Assert.That(GetContext().GetPstateFlag(PState.CFlag), Is.EqualTo(result < w1)); + Assert.That(GetContext().GetPstateFlag(PState.VFlag), Is.EqualTo((overflow >> 31) != 0)); + } + break; + case 12: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1 | w2)); + break; + case 13: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1 * w2)); + break; + case 14: + Assert.That(GetContext().GetX(1), Is.EqualTo(w1 & ~w2)); + break; + case 15: + Assert.That(GetContext().GetX(1), Is.EqualTo(~w2)); + break; + } + } } }