diff options
| author | LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> | 2018-04-18 22:22:45 +0200 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-04-18 17:22:45 -0300 |
| commit | e9a96e3522ee7620b525d210915a0e45510ea528 (patch) | |
| tree | 02d9d99b14cc635fd078a552b493e1bc2c22c8d8 /Ryujinx.Tests/Cpu/Tester | |
| parent | 8b75080639204b667e4b78acd3a88090f15bc651 (diff) | |
Add 151 complete tests for 71 base instructions of types: Alu; AluImm; AluRs; AluRx; Bfm; CcmpImm; CcmpReg; Csel; Mov; Mul. (#80)
* Add files via upload
* Update Ryujinx.Tests.csproj
Diffstat (limited to 'Ryujinx.Tests/Cpu/Tester')
| -rw-r--r-- | Ryujinx.Tests/Cpu/Tester/Instructions.cs | 1682 | ||||
| -rw-r--r-- | Ryujinx.Tests/Cpu/Tester/Pseudocode.cs | 958 | ||||
| -rw-r--r-- | Ryujinx.Tests/Cpu/Tester/Types/Bits.cs | 248 | ||||
| -rw-r--r-- | Ryujinx.Tests/Cpu/Tester/Types/Integer.cs | 42 |
4 files changed, 2930 insertions, 0 deletions
diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs new file mode 100644 index 00000000..7a51923f --- /dev/null +++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs @@ -0,0 +1,1682 @@ +// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Instructions.cs + +// https://meriac.github.io/archex/A64_v83A_ISA/index.xml +/* https://meriac.github.io/archex/A64_v83A_ISA/fpsimdindex.xml */ + +using System.Numerics; + +namespace Ryujinx.Tests.Cpu.Tester +{ + using Types; + + using static AArch64; + using static Shared; + + internal static class Base + { +#region "Alu" + // https://meriac.github.io/archex/A64_v83A_ISA/cls_int.xml + public static void Cls(bool sf, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits operand1 = X(datasize, n); + + BigInteger result = (BigInteger)CountLeadingSignBits(operand1); + + X(d, result.SubBigInteger(datasize - 1, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/clz_int.xml + public static void Clz(bool sf, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits operand1 = X(datasize, n); + + BigInteger result = (BigInteger)CountLeadingZeroBits(operand1); + + X(d, result.SubBigInteger(datasize - 1, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/rbit_int.xml + public static void Rbit(bool sf, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result = new Bits(datasize); + Bits operand = X(datasize, n); + + for (int i = 0; i <= datasize - 1; i++) + { + result[datasize - 1 - i] = operand[i]; + } + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/rev16_int.xml + public static void Rev16(bool sf, Bits Rn, Bits Rd) + { + /* Bits opc = "01"; */ + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + int container_size = 16; + + /* Operation */ + Bits result = new Bits(datasize); + Bits operand = X(datasize, n); + + int containers = datasize / container_size; + int elements_per_container = container_size / 8; + int index = 0; + int rev_index; + + for (int c = 0; c <= containers - 1; c++) + { + rev_index = index + ((elements_per_container - 1) * 8); + + for (int e = 0; e <= elements_per_container - 1; e++) + { + result[rev_index + 7, rev_index] = operand[index + 7, index]; + + index = index + 8; + rev_index = rev_index - 8; + } + } + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/rev32_int.xml + // (https://meriac.github.io/archex/A64_v83A_ISA/rev.xml) + public static void Rev32(bool sf, Bits Rn, Bits Rd) + { + /* Bits opc = "10"; */ + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + int container_size = 32; + + /* Operation */ + Bits result = new Bits(datasize); + Bits operand = X(datasize, n); + + int containers = datasize / container_size; + int elements_per_container = container_size / 8; + int index = 0; + int rev_index; + + for (int c = 0; c <= containers - 1; c++) + { + rev_index = index + ((elements_per_container - 1) * 8); + + for (int e = 0; e <= elements_per_container - 1; e++) + { + result[rev_index + 7, rev_index] = operand[index + 7, index]; + + index = index + 8; + rev_index = rev_index - 8; + } + } + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/rev64_rev.xml + // (https://meriac.github.io/archex/A64_v83A_ISA/rev.xml) + public static void Rev64(Bits Rn, Bits Rd) + { + /* Bits opc = "11"; */ + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + int container_size = 64; + + /* Operation */ + Bits result = new Bits(64); + Bits operand = X(64, n); + + int containers = 64 / container_size; + int elements_per_container = container_size / 8; + int index = 0; + int rev_index; + + for (int c = 0; c <= containers - 1; c++) + { + rev_index = index + ((elements_per_container - 1) * 8); + + for (int e = 0; e <= elements_per_container - 1; e++) + { + result[rev_index + 7, rev_index] = operand[index + 7, index]; + + index = index + 8; + rev_index = rev_index - 8; + } + } + + X(d, result); + } +#endregion + +#region "AluImm" + // https://meriac.github.io/archex/A64_v83A_ISA/add_addsub_imm.xml + public static void Add_Imm(bool sf, Bits shift, Bits imm12, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits imm; + + switch (shift) + { + default: + case Bits bits when bits == "00": + imm = ZeroExtend(imm12, datasize); + break; + case Bits bits when bits == "01": + imm = ZeroExtend(Bits.Concat(imm12, Zeros(12)), datasize); + break; + /* when '1x' ReservedValue(); */ + } + + /* Operation */ + Bits result; + Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n)); + + (result, _) = AddWithCarry(datasize, operand1, imm, false); + + if (d == 31) + { + SP(result); + } + else + { + X(d, result); + } + } + + // https://meriac.github.io/archex/A64_v83A_ISA/adds_addsub_imm.xml + public static void Adds_Imm(bool sf, Bits shift, Bits imm12, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits imm; + + switch (shift) + { + default: + case Bits bits when bits == "00": + imm = ZeroExtend(imm12, datasize); + break; + case Bits bits when bits == "01": + imm = ZeroExtend(Bits.Concat(imm12, Zeros(12)), datasize); + break; + /* when '1x' ReservedValue(); */ + } + + /* Operation */ + Bits result; + Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n)); + Bits nzcv; + + (result, nzcv) = AddWithCarry(datasize, operand1, imm, false); + + PSTATE.NZCV(nzcv); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/and_log_imm.xml + public static void And_Imm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits imm; + + /* if sf == '0' && N != '0' then ReservedValue(); */ + + (imm, _) = DecodeBitMasks(datasize, N, imms, immr, true); + + /* Operation */ + Bits operand1 = X(datasize, n); + + Bits result = AND(operand1, imm); + + if (d == 31) + { + SP(result); + } + else + { + X(d, result); + } + } + + // https://meriac.github.io/archex/A64_v83A_ISA/ands_log_imm.xml + public static void Ands_Imm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits imm; + + /* if sf == '0' && N != '0' then ReservedValue(); */ + + (imm, _) = DecodeBitMasks(datasize, N, imms, immr, true); + + /* Operation */ + Bits operand1 = X(datasize, n); + + Bits result = AND(operand1, imm); + + PSTATE.NZCV(result[datasize - 1], IsZeroBit(result), false, false); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/eor_log_imm.xml + public static void Eor_Imm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits imm; + + /* if sf == '0' && N != '0' then ReservedValue(); */ + + (imm, _) = DecodeBitMasks(datasize, N, imms, immr, true); + + /* Operation */ + Bits operand1 = X(datasize, n); + + Bits result = EOR(operand1, imm); + + if (d == 31) + { + SP(result); + } + else + { + X(d, result); + } + } + + // https://meriac.github.io/archex/A64_v83A_ISA/orr_log_imm.xml + public static void Orr_Imm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits imm; + + /* if sf == '0' && N != '0' then ReservedValue(); */ + + (imm, _) = DecodeBitMasks(datasize, N, imms, immr, true); + + /* Operation */ + Bits operand1 = X(datasize, n); + + Bits result = OR(operand1, imm); + + if (d == 31) + { + SP(result); + } + else + { + X(d, result); + } + } + + // https://meriac.github.io/archex/A64_v83A_ISA/sub_addsub_imm.xml + public static void Sub_Imm(bool sf, Bits shift, Bits imm12, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits imm; + + switch (shift) + { + default: + case Bits bits when bits == "00": + imm = ZeroExtend(imm12, datasize); + break; + case Bits bits when bits == "01": + imm = ZeroExtend(Bits.Concat(imm12, Zeros(12)), datasize); + break; + /* when '1x' ReservedValue(); */ + } + + /* Operation */ + Bits result; + Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n)); + Bits operand2 = NOT(imm); + + (result, _) = AddWithCarry(datasize, operand1, operand2, true); + + if (d == 31) + { + SP(result); + } + else + { + X(d, result); + } + } + + // https://meriac.github.io/archex/A64_v83A_ISA/subs_addsub_imm.xml + public static void Subs_Imm(bool sf, Bits shift, Bits imm12, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits imm; + + switch (shift) + { + default: + case Bits bits when bits == "00": + imm = ZeroExtend(imm12, datasize); + break; + case Bits bits when bits == "01": + imm = ZeroExtend(Bits.Concat(imm12, Zeros(12)), datasize); + break; + /* when '1x' ReservedValue(); */ + } + + /* Operation */ + Bits result; + Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n)); + Bits operand2 = NOT(imm); + Bits nzcv; + + (result, nzcv) = AddWithCarry(datasize, operand1, operand2, true); + + PSTATE.NZCV(nzcv); + + X(d, result); + } +#endregion + +#region "AluRs" + // https://meriac.github.io/archex/A64_v83A_ISA/adc.xml + public static void Adc(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + (result, _) = AddWithCarry(datasize, operand1, operand2, PSTATE.C); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/adcs.xml + public static void Adcs(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + Bits nzcv; + + (result, nzcv) = AddWithCarry(datasize, operand1, operand2, PSTATE.C); + + PSTATE.NZCV(nzcv); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/add_addsub_shift.xml + public static void Add_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if shift == '11' then ReservedValue(); */ + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + (result, _) = AddWithCarry(datasize, operand1, operand2, false); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/adds_addsub_shift.xml + public static void Adds_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if shift == '11' then ReservedValue(); */ + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + Bits nzcv; + + (result, nzcv) = AddWithCarry(datasize, operand1, operand2, false); + + PSTATE.NZCV(nzcv); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/and_log_shift.xml + public static void And_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + Bits result = AND(operand1, operand2); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/ands_log_shift.xml + public static void Ands_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + Bits result = AND(operand1, operand2); + + PSTATE.NZCV(result[datasize - 1], IsZeroBit(result), false, false); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/asrv.xml + public static void Asrv(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + Bits op2 = "10"; + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + ShiftType shift_type = DecodeShift(op2); + + /* Operation */ + Bits operand2 = X(datasize, m); + + Bits result = ShiftReg(datasize, n, shift_type, (int)(UInt(operand2) % datasize)); // BigInteger.Modulus Operator (BigInteger, BigInteger) + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/bic_log_shift.xml + public static void Bic(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + operand2 = NOT(operand2); + + Bits result = AND(operand1, operand2); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/bics.xml + public static void Bics(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + operand2 = NOT(operand2); + + Bits result = AND(operand1, operand2); + + PSTATE.NZCV(result[datasize - 1], IsZeroBit(result), false, false); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/crc32.xml + public static void Crc32(bool sf, Bits Rm, Bits sz, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if sf == '1' && sz != '11' then UnallocatedEncoding(); */ + /* if sf == '0' && sz == '11' then UnallocatedEncoding(); */ + + int size = 8 << (int)UInt(sz); + + /* Operation */ + /* if !HaveCRCExt() then UnallocatedEncoding(); */ + + Bits acc = X(32, n); // accumulator + Bits val = X(size, m); // input value + Bits poly = new Bits(0x04C11DB7u); + + Bits tempacc = Bits.Concat(BitReverse(acc), Zeros(size)); + Bits tempval = Bits.Concat(BitReverse(val), Zeros(32)); + + // Poly32Mod2 on a bitstring does a polynomial Modulus over {0,1} operation + X(d, BitReverse(Poly32Mod2(EOR(tempacc, tempval), poly))); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/crc32c.xml + public static void Crc32c(bool sf, Bits Rm, Bits sz, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if sf == '1' && sz != '11' then UnallocatedEncoding(); */ + /* if sf == '0' && sz == '11' then UnallocatedEncoding(); */ + + int size = 8 << (int)UInt(sz); + + /* Operation */ + /* if !HaveCRCExt() then UnallocatedEncoding(); */ + + Bits acc = X(32, n); // accumulator + Bits val = X(size, m); // input value + Bits poly = new Bits(0x1EDC6F41u); + + Bits tempacc = Bits.Concat(BitReverse(acc), Zeros(size)); + Bits tempval = Bits.Concat(BitReverse(val), Zeros(32)); + + // Poly32Mod2 on a bitstring does a polynomial Modulus over {0,1} operation + X(d, BitReverse(Poly32Mod2(EOR(tempacc, tempval), poly))); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/eon.xml + public static void Eon(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + operand2 = NOT(operand2); + + Bits result = EOR(operand1, operand2); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/eor_log_shift.xml + public static void Eor_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + Bits result = EOR(operand1, operand2); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/extr.xml + public static void Extr(bool sf, bool N, Bits Rm, Bits imms, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if N != sf then UnallocatedEncoding(); */ + /* if sf == '0' && imms<5> == '1' then ReservedValue(); */ + + int lsb = (int)UInt(imms); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + Bits concat = Bits.Concat(operand1, operand2); + + Bits result = concat[lsb + datasize - 1, lsb]; + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/lslv.xml + public static void Lslv(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + Bits op2 = "00"; + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + ShiftType shift_type = DecodeShift(op2); + + /* Operation */ + Bits operand2 = X(datasize, m); + + Bits result = ShiftReg(datasize, n, shift_type, (int)(UInt(operand2) % datasize)); // BigInteger.Modulus Operator (BigInteger, BigInteger) + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/lsrv.xml + public static void Lsrv(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + Bits op2 = "01"; + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + ShiftType shift_type = DecodeShift(op2); + + /* Operation */ + Bits operand2 = X(datasize, m); + + Bits result = ShiftReg(datasize, n, shift_type, (int)(UInt(operand2) % datasize)); // BigInteger.Modulus Operator (BigInteger, BigInteger) + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/orn_log_shift.xml + public static void Orn(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + operand2 = NOT(operand2); + + Bits result = OR(operand1, operand2); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/orr_log_shift.xml + public static void Orr_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + Bits result = OR(operand1, operand2); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/rorv.xml + public static void Rorv(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + Bits op2 = "11"; + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + ShiftType shift_type = DecodeShift(op2); + + /* Operation */ + Bits operand2 = X(datasize, m); + + Bits result = ShiftReg(datasize, n, shift_type, (int)(UInt(operand2) % datasize)); // BigInteger.Modulus Operator (BigInteger, BigInteger) + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/sbc.xml + public static void Sbc(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + operand2 = NOT(operand2); + + (result, _) = AddWithCarry(datasize, operand1, operand2, PSTATE.C); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/sbcs.xml + public static void Sbcs(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + Bits nzcv; + + operand2 = NOT(operand2); + + (result, nzcv) = AddWithCarry(datasize, operand1, operand2, PSTATE.C); + + PSTATE.NZCV(nzcv); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/sdiv.xml + public static void Sdiv(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + BigInteger result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + if (IsZero(operand2)) + { + result = (BigInteger)0m; + } + else + { + result = RoundTowardsZero(Real(Int(operand1, false)) / Real(Int(operand2, false))); + } + + X(d, result.SubBigInteger(datasize - 1, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/sub_addsub_shift.xml + public static void Sub_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if shift == '11' then ReservedValue(); */ + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + + operand2 = NOT(operand2); + + (result, _) = AddWithCarry(datasize, operand1, operand2, true); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/subs_addsub_shift.xml + public static void Subs_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* if shift == '11' then ReservedValue(); */ + /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ + + ShiftType shift_type = DecodeShift(shift); + int shift_amount = (int)UInt(imm6); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount); + Bits nzcv; + + operand2 = NOT(operand2); + + (result, nzcv) = AddWithCarry(datasize, operand1, operand2, true); + + PSTATE.NZCV(nzcv); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/udiv.xml + public static void Udiv(bool sf, Bits Rm, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + BigInteger result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + if (IsZero(operand2)) + { + result = (BigInteger)0m; + } + else + { + result = RoundTowardsZero(Real(Int(operand1, true)) / Real(Int(operand2, true))); + } + + X(d, result.SubBigInteger(datasize - 1, 0)); + } +#endregion + +#region "AluRx" + // https://meriac.github.io/archex/A64_v83A_ISA/add_addsub_ext.xml + public static void Add_Rx(bool sf, Bits Rm, Bits option, Bits imm3, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + ExtendType extend_type = DecodeRegExtend(option); + int shift = (int)UInt(imm3); + + /* if shift > 4 then ReservedValue(); */ + + /* Operation */ + Bits result; + Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n)); + Bits operand2 = ExtendReg(datasize, m, extend_type, shift); + + (result, _) = AddWithCarry(datasize, operand1, operand2, false); + + if (d == 31) + { + SP(result); + } + else + { + X(d, result); + } + } + + // https://meriac.github.io/archex/A64_v83A_ISA/adds_addsub_ext.xml + public static void Adds_Rx(bool sf, Bits Rm, Bits option, Bits imm3, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + ExtendType extend_type = DecodeRegExtend(option); + int shift = (int)UInt(imm3); + + /* if shift > 4 then ReservedValue(); */ + + /* Operation */ + Bits result; + Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n)); + Bits operand2 = ExtendReg(datasize, m, extend_type, shift); + Bits nzcv; + + (result, nzcv) = AddWithCarry(datasize, operand1, operand2, false); + + PSTATE.NZCV(nzcv); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/sub_addsub_ext.xml + public static void Sub_Rx(bool sf, Bits Rm, Bits option, Bits imm3, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + ExtendType extend_type = DecodeRegExtend(option); + int shift = (int)UInt(imm3); + + /* if shift > 4 then ReservedValue(); */ + + /* Operation */ + Bits result; + Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n)); + Bits operand2 = ExtendReg(datasize, m, extend_type, shift); + + operand2 = NOT(operand2); + + (result, _) = AddWithCarry(datasize, operand1, operand2, true); + + if (d == 31) + { + SP(result); + } + else + { + X(d, result); + } + } + + // https://meriac.github.io/archex/A64_v83A_ISA/subs_addsub_ext.xml + public static void Subs_Rx(bool sf, Bits Rm, Bits option, Bits imm3, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + ExtendType extend_type = DecodeRegExtend(option); + int shift = (int)UInt(imm3); + + /* if shift > 4 then ReservedValue(); */ + + /* Operation */ + Bits result; + Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n)); + Bits operand2 = ExtendReg(datasize, m, extend_type, shift); + Bits nzcv; + + operand2 = NOT(operand2); + + (result, nzcv) = AddWithCarry(datasize, operand1, operand2, true); + + PSTATE.NZCV(nzcv); + + X(d, result); + } +#endregion + +#region "Bfm" + // https://meriac.github.io/archex/A64_v83A_ISA/bfm.xml + public static void Bfm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + int R; + Bits wmask; + Bits tmask; + + /* if sf == '1' && N != '1' then ReservedValue(); */ + /* if sf == '0' && (N != '0' || immr<5> != '0' || imms<5> != '0') then ReservedValue(); */ + + R = (int)UInt(immr); + (wmask, tmask) = DecodeBitMasks(datasize, N, imms, immr, false); + + /* Operation */ + Bits dst = X(datasize, d); + Bits src = X(datasize, n); + + // perform bitfield move on low bits + Bits bot = OR(AND(dst, NOT(wmask)), AND(ROR(src, R), wmask)); + + // combine extension bits and result bits + X(d, OR(AND(dst, NOT(tmask)), AND(bot, tmask))); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/sbfm.xml + public static void Sbfm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + int R; + int S; + Bits wmask; + Bits tmask; + + /* if sf == '1' && N != '1' then ReservedValue(); */ + /* if sf == '0' && (N != '0' || immr<5> != '0' || imms<5> != '0') then ReservedValue(); */ + + R = (int)UInt(immr); + S = (int)UInt(imms); + (wmask, tmask) = DecodeBitMasks(datasize, N, imms, immr, false); + + /* Operation */ + Bits src = X(datasize, n); + + // perform bitfield move on low bits + Bits bot = AND(ROR(src, R), wmask); + + // determine extension bits (sign, zero or dest register) + Bits top = Replicate(datasize, src[S]); + + // combine extension bits and result bits + X(d, OR(AND(top, NOT(tmask)), AND(bot, tmask))); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/ubfm.xml + public static void Ubfm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + int R; + Bits wmask; + Bits tmask; + + /* if sf == '1' && N != '1' then ReservedValue(); */ + /* if sf == '0' && (N != '0' || immr<5> != '0' || imms<5> != '0') then ReservedValue(); */ + + R = (int)UInt(immr); + (wmask, tmask) = DecodeBitMasks(datasize, N, imms, immr, false); + + /* Operation */ + Bits src = X(datasize, n); + + // perform bitfield move on low bits + Bits bot = AND(ROR(src, R), wmask); + + // combine extension bits and result bits + X(d, AND(bot, tmask)); + } +#endregion + +#region "CcmpImm" + // https://meriac.github.io/archex/A64_v83A_ISA/ccmn_imm.xml + public static void Ccmn_Imm(bool sf, Bits imm5, Bits cond, Bits Rn, Bits nzcv) + { + /* Decode */ + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits flags = nzcv; + Bits imm = ZeroExtend(imm5, datasize); + + /* Operation */ + Bits operand1 = X(datasize, n); + + if (ConditionHolds(cond)) + { + (_, flags) = AddWithCarry(datasize, operand1, imm, false); + } + + PSTATE.NZCV(flags); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/ccmp_imm.xml + public static void Ccmp_Imm(bool sf, Bits imm5, Bits cond, Bits Rn, Bits nzcv) + { + /* Decode */ + int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); + + Bits flags = nzcv; + Bits imm = ZeroExtend(imm5, datasize); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2; + + if (ConditionHolds(cond)) + { + operand2 = NOT(imm); + (_, flags) = AddWithCarry(datasize, operand1, operand2, true); + } + + PSTATE.NZCV(flags); + } +#endregion + +#region "CcmpReg" + // https://meriac.github.io/archex/A64_v83A_ISA/ccmn_reg.xml + public static void Ccmn_Reg(bool sf, Bits Rm, Bits cond, Bits Rn, Bits nzcv) + { + /* Decode */ + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + Bits flags = nzcv; + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + if (ConditionHolds(cond)) + { + (_, flags) = AddWithCarry(datasize, operand1, operand2, false); + } + + PSTATE.NZCV(flags); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/ccmp_reg.xml + public static void Ccmp_Reg(bool sf, Bits Rm, Bits cond, Bits Rn, Bits nzcv) + { + /* Decode */ + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + Bits flags = nzcv; + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + if (ConditionHolds(cond)) + { + operand2 = NOT(operand2); + (_, flags) = AddWithCarry(datasize, operand1, operand2, true); + } + + PSTATE.NZCV(flags); + } +#endregion + +#region "Csel" + // https://meriac.github.io/archex/A64_v83A_ISA/csel.xml + public static void Csel(bool sf, Bits Rm, Bits cond, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + if (ConditionHolds(cond)) + { + result = operand1; + } + else + { + result = operand2; + } + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/csinc.xml + public static void Csinc(bool sf, Bits Rm, Bits cond, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + if (ConditionHolds(cond)) + { + result = operand1; + } + else + { + result = operand2 + 1; + } + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/csinv.xml + public static void Csinv(bool sf, Bits Rm, Bits cond, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + if (ConditionHolds(cond)) + { + result = operand1; + } + else + { + result = NOT(operand2); + } + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/csneg.xml + public static void Csneg(bool sf, Bits Rm, Bits cond, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits result; + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + + if (ConditionHolds(cond)) + { + result = operand1; + } + else + { + result = NOT(operand2); + result = result + 1; + } + + X(d, result); + } +#endregion + +#region "Mov" + // https://meriac.github.io/archex/A64_v83A_ISA/movk.xml + public static void Movk(bool sf, Bits hw, Bits imm16, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ + + int pos = (int)UInt(Bits.Concat(hw, "0000")); + + /* Operation */ + Bits result = X(datasize, d); + + result[pos + 15, pos] = imm16; + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/movn.xml + public static void Movn(bool sf, Bits hw, Bits imm16, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ + + int pos = (int)UInt(Bits.Concat(hw, "0000")); + + /* Operation */ + Bits result = Zeros(datasize); + + result[pos + 15, pos] = imm16; + result = NOT(result); + + X(d, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/movz.xml + public static void Movz(bool sf, Bits hw, Bits imm16, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int datasize = (sf ? 64 : 32); + + /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ + + int pos = (int)UInt(Bits.Concat(hw, "0000")); + + /* Operation */ + Bits result = Zeros(datasize); + + result[pos + 15, pos] = imm16; + + X(d, result); + } +#endregion + +#region "Mul" + // https://meriac.github.io/archex/A64_v83A_ISA/madd.xml + public static void Madd(bool sf, Bits Rm, Bits Ra, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int a = (int)UInt(Ra); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + Bits operand3 = X(datasize, a); + + BigInteger result = UInt(operand3) + (UInt(operand1) * UInt(operand2)); + + X(d, result.SubBigInteger(datasize - 1, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/msub.xml + public static void Msub(bool sf, Bits Rm, Bits Ra, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int a = (int)UInt(Ra); + int datasize = (sf ? 64 : 32); + + /* Operation */ + Bits operand1 = X(datasize, n); + Bits operand2 = X(datasize, m); + Bits operand3 = X(datasize, a); + + BigInteger result = UInt(operand3) - (UInt(operand1) * UInt(operand2)); + + X(d, result.SubBigInteger(datasize - 1, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/smaddl.xml + public static void Smaddl(Bits Rm, Bits Ra, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int a = (int)UInt(Ra); + + /* Operation */ + Bits operand1 = X(32, n); + Bits operand2 = X(32, m); + Bits operand3 = X(64, a); + + BigInteger result = Int(operand3, false) + (Int(operand1, false) * Int(operand2, false)); + + X(d, result.SubBigInteger(63, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/umaddl.xml + public static void Umaddl(Bits Rm, Bits Ra, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int a = (int)UInt(Ra); + + /* Operation */ + Bits operand1 = X(32, n); + Bits operand2 = X(32, m); + Bits operand3 = X(64, a); + + BigInteger result = Int(operand3, true) + (Int(operand1, true) * Int(operand2, true)); + + X(d, result.SubBigInteger(63, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/smsubl.xml + public static void Smsubl(Bits Rm, Bits Ra, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int a = (int)UInt(Ra); + + /* Operation */ + Bits operand1 = X(32, n); + Bits operand2 = X(32, m); + Bits operand3 = X(64, a); + + BigInteger result = Int(operand3, false) - (Int(operand1, false) * Int(operand2, false)); + + X(d, result.SubBigInteger(63, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/umsubl.xml + public static void Umsubl(Bits Rm, Bits Ra, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + int a = (int)UInt(Ra); + + /* Operation */ + Bits operand1 = X(32, n); + Bits operand2 = X(32, m); + Bits operand3 = X(64, a); + + BigInteger result = Int(operand3, true) - (Int(operand1, true) * Int(operand2, true)); + + X(d, result.SubBigInteger(63, 0)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/smulh.xml + public static void Smulh(Bits Rm, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* Operation */ + Bits operand1 = X(64, n); + Bits operand2 = X(64, m); + + BigInteger result = Int(operand1, false) * Int(operand2, false); + + X(d, result.SubBigInteger(127, 64)); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/umulh.xml + public static void Umulh(Bits Rm, Bits Rn, Bits Rd) + { + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* Operation */ + Bits operand1 = X(64, n); + Bits operand2 = X(64, m); + + BigInteger result = Int(operand1, true) * Int(operand2, true); + + X(d, result.SubBigInteger(127, 64)); + } +#endregion + } + + /* + internal static class Advanced + { + } + */ +} diff --git a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs new file mode 100644 index 00000000..1da17c49 --- /dev/null +++ b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs @@ -0,0 +1,958 @@ +// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Pseudocode.cs + +// https://meriac.github.io/archex/A64_v83A_ISA/shared_pseudocode.xml +// https://alastairreid.github.io/asl-lexical-syntax/ + +// | ------------------------|----------------------------------- | +// | ASL | C# | +// | ------------------------|----------------------------------- | +// | bit, bits(1); boolean | bool | +// | bits | Bits | +// | integer | BigInteger, int | +// | real | decimal | +// | ------------------------|----------------------------------- | +// | '0'; FALSE | false | +// | '1'; TRUE | true | +// | '010' | "010" | +// | bitsX IN {bitsY, bitsZ} | (bitsX == bitsY || bitsX == bitsZ) | +// | DIV | / | +// | MOD | % | +// | ------------------------|----------------------------------- | + +using System; +using System.Numerics; + +namespace Ryujinx.Tests.Cpu.Tester +{ + using Types; + + using static Shared; + + internal static class AArch64 + { +#region "exceptions/exceptions/" + /* #AArch64.ResetControlRegisters.1 */ + public static void ResetControlRegisters(bool cold_reset) + { + PSTATE.N = cold_reset; + PSTATE.Z = cold_reset; + PSTATE.C = cold_reset; + PSTATE.V = cold_reset; + } + + /* */ + public static void TakeReset(bool cold_reset) + { + /* assert !HighestELUsingAArch32(); */ + + // Enter the highest implemented Exception level in AArch64 state + if (HaveEL(EL3)) + { + PSTATE.EL = EL3; + } + else if (HaveEL(EL2)) + { + PSTATE.EL = EL2; + } + else + { + PSTATE.EL = EL1; + } + + // Reset the system registers and other system components + AArch64.ResetControlRegisters(cold_reset); + + // Reset all other PSTATE fields + PSTATE.SP = true; // Select stack pointer + + // All registers, bits and fields not reset by the above pseudocode or by the BranchTo() call + // below are UNKNOWN bitstrings after reset. In particular, the return information registers + // ELR_ELx and SPSR_ELx have UNKNOWN values, so that it + // is impossible to return from a reset in an architecturally defined way. + AArch64.ResetGeneralRegisters(); + AArch64.ResetSpecialRegisters(); + } +#endregion + +#region "functions/registers/" + /* #AArch64.ResetGeneralRegisters.0 */ + public static void ResetGeneralRegisters() + { + for (int i = 0; i <= 30; i++) + { + /* X[i] = bits(64) UNKNOWN; */ + _R[i].SetAll(false); + } + } + + /* #AArch64.ResetSpecialRegisters.0 */ + public static void ResetSpecialRegisters() + { + // AArch64 special registers + /* SP_EL0 = bits(64) UNKNOWN; */ + SP_EL0.SetAll(false); + /* SP_EL1 = bits(64) UNKNOWN; */ + SP_EL1.SetAll(false); + } + + // #impl-aarch64.SP.write.0 + public static void SP(Bits value) + { + /* int width = value.Count; */ + + /* assert width IN {32,64}; */ + + if (!PSTATE.SP) + { + SP_EL0 = ZeroExtend(64, value); + } + else + { + switch (PSTATE.EL) + { + case Bits bits when bits == EL0: + SP_EL0 = ZeroExtend(64, value); + break; + default: + case Bits bits when bits == EL1: + SP_EL1 = ZeroExtend(64, value); + break; + /*case Bits bits when bits == EL2: + SP_EL2 = ZeroExtend(64, value); + break; + case Bits bits when bits == EL3: + SP_EL3 = ZeroExtend(64, value); + break;*/ + } + } + } + + // #impl-aarch64.SP.read.0 + public static Bits SP(int width) + { + /* assert width IN {8,16,32,64}; */ + + if (!PSTATE.SP) + { + return SP_EL0[width - 1, 0]; + } + else + { + switch (PSTATE.EL) + { + case Bits bits when bits == EL0: + return SP_EL0[width - 1, 0]; + default: + case Bits bits when bits == EL1: + return SP_EL1[width - 1, 0]; + /*case Bits bits when bits == EL2: + return SP_EL2[width - 1, 0]; + case Bits bits when bits == EL3: + return SP_EL3[width - 1, 0];*/ + } + } + } + + // #impl-aarch64.X.write.1 + public static void X(int n, Bits value) + { + /* int width = value.Count; */ + + /* assert n >= 0 && n <= 31; */ + /* assert width IN {32,64}; */ + + if (n != 31) + { + _R[n] = ZeroExtend(64, value); + } + } + + /* #impl-aarch64.X.read.1 */ + public static Bits X(int width, int n) + { + /* assert n >= 0 && n <= 31; */ + /* assert width IN {8,16,32,64}; */ + + if (n != 31) + { + return _R[n][width - 1, 0]; + } + else + { + return Zeros(width); + } + } +#endregion + +#region "instrs/extendreg/" + /* #impl-aarch64.DecodeRegExtend.1 */ + public static ExtendType DecodeRegExtend(Bits op) + { + switch (op) + { + default: + case Bits bits when bits == "000": + return ExtendType.ExtendType_UXTB; + case Bits bits when bits == "001": + return ExtendType.ExtendType_UXTH; + case Bits bits when bits == "010": + return ExtendType.ExtendType_UXTW; + case Bits bits when bits == "011": + return ExtendType.ExtendType_UXTX; + case Bits bits when bits == "100": + return ExtendType.ExtendType_SXTB; + case Bits bits when bits == "101": + return ExtendType.ExtendType_SXTH; + case Bits bits when bits == "110": + return ExtendType.ExtendType_SXTW; + case Bits bits when bits == "111": + return ExtendType.ExtendType_SXTX; + } + } + + /* #impl-aarch64.ExtendReg.3 */ + public static Bits ExtendReg(int N, int reg, ExtendType type, int shift) + { + /* assert shift >= 0 && shift <= 4; */ + Bits val = X(N, reg); + bool unsigned; + int len; + + switch (type) + { + default: + case ExtendType.ExtendType_SXTB: + unsigned = false; len = 8; + break; + case ExtendType.ExtendType_SXTH: + unsigned = false; len = 16; + break; + case ExtendType.ExtendType_SXTW: + unsigned = false; len = 32; + break; + case ExtendType.ExtendType_SXTX: + unsigned = false; len = 64; + break; + case ExtendType.ExtendType_UXTB: + unsigned = true; len = 8; + break; + case ExtendType.ExtendType_UXTH: + unsigned = true; len = 16; + break; + case ExtendType.ExtendType_UXTW: + unsigned = true; len = 32; + break; + case ExtendType.ExtendType_UXTX: + unsigned = true; len = 64; + break; + } + + // Note the extended width of the intermediate value and + // that sign extension occurs from bit <len+shift-1>, not + // from bit <len-1>. This is equivalent to the instruction + // [SU]BFIZ Rtmp, Rreg, #shift, #len + // It may also be seen as a sign/zero extend followed by a shift: + // LSL(Extend(val<len-1:0>, N, unsigned), shift); + + len = Min(len, N - shift); + return Extend(Bits.Concat(val[len - 1, 0], Zeros(shift)), N, unsigned); + } + + // #ExtendType + public enum ExtendType {ExtendType_SXTB, ExtendType_SXTH, ExtendType_SXTW, ExtendType_SXTX, + ExtendType_UXTB, ExtendType_UXTH, ExtendType_UXTW, ExtendType_UXTX}; +#endregion + +#region "instrs/integer/bitmasks/" + /* #impl-aarch64.DecodeBitMasks.4 */ + public static (Bits, Bits) DecodeBitMasks(int M, bool immN, Bits imms, Bits immr, bool immediate) + { + Bits tmask, wmask; + Bits tmask_and, wmask_and; + Bits tmask_or, wmask_or; + Bits levels; + + // Compute log2 of element size + // 2^len must be in range [2, M] + int len = HighestSetBit(Bits.Concat(immN, NOT(imms))); + /* if len < 1 then ReservedValue(); */ + /* assert M >= (1 << len); */ + + // Determine S, R and S - R parameters + levels = ZeroExtend(Ones(len), 6); + + // For logical immediates an all-ones value of S is reserved + // since it would generate a useless all-ones result (many times) + /* if immediate && (imms AND levels) == levels then ReservedValue(); */ + + BigInteger S = UInt(AND(imms, levels)); + BigInteger R = UInt(AND(immr, levels)); + BigInteger diff = S - R; // 6-bit subtract with borrow + + // Compute "top mask" + tmask_and = OR(diff.SubBigInteger(5, 0), NOT(levels)); + tmask_or = AND(diff.SubBigInteger(5, 0), levels); + + tmask = Ones(64); + tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[0], 1), Ones( 1)), 32)), Replicate(Bits.Concat(Zeros( 1), Replicate(tmask_or[0], 1)), 32)); + tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[1], 2), Ones( 2)), 16)), Replicate(Bits.Concat(Zeros( 2), Replicate(tmask_or[1], 2)), 16)); + tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[2], 4), Ones( 4)), 8)), Replicate(Bits.Concat(Zeros( 4), Replicate(tmask_or[2], 4)), 8)); + tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[3], 8), Ones( 8)), 4)), Replicate(Bits.Concat(Zeros( 8), Replicate(tmask_or[3], 8)), 4)); + tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[4], 16), Ones(16)), 2)), Replicate(Bits.Concat(Zeros(16), Replicate(tmask_or[4], 16)), 2)); + tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[5], 32), Ones(32)), 1)), Replicate(Bits.Concat(Zeros(32), Replicate(tmask_or[5], 32)), 1)); + + // Compute "wraparound mask" + wmask_and = OR(immr, NOT(levels)); + wmask_or = AND(immr, levels); + + wmask = Zeros(64); + wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones( 1), Replicate(wmask_and[0], 1)), 32)), Replicate(Bits.Concat(Replicate(wmask_or[0], 1), Zeros( 1)), 32)); + wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones( 2), Replicate(wmask_and[1], 2)), 16)), Replicate(Bits.Concat(Replicate(wmask_or[1], 2), Zeros( 2)), 16)); + wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones( 4), Replicate(wmask_and[2], 4)), 8)), Replicate(Bits.Concat(Replicate(wmask_or[2], 4), Zeros( 4)), 8)); + wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones( 8), Replicate(wmask_and[3], 8)), 4)), Replicate(Bits.Concat(Replicate(wmask_or[3], 8), Zeros( 8)), 4)); + wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones(16), Replicate(wmask_and[4], 16)), 2)), Replicate(Bits.Concat(Replicate(wmask_or[4], 16), Zeros(16)), 2)); + wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones(32), Replicate(wmask_and[5], 32)), 1)), Replicate(Bits.Concat(Replicate(wmask_or[5], 32), Zeros(32)), 1)); + + if (diff.SubBigInteger(6)) // borrow from S - R + { + wmask = AND(wmask, tmask); + } + else + { + wmask = OR(wmask, tmask); + } + + return (wmask[M - 1, 0], tmask[M - 1, 0]); + } +#endregion + +#region "instrs/integer/shiftreg/" + /* #impl-aarch64.DecodeShift.1 */ + public static ShiftType DecodeShift(Bits op) + { + switch (op) + { + default: + case Bits bits when bits == "00": + return ShiftType.ShiftType_LSL; + case Bits bits when bits == "01": + return ShiftType.ShiftType_LSR; + case Bits bits when bits == "10": + return ShiftType.ShiftType_ASR; + case Bits bits when bits == "11": + return ShiftType.ShiftType_ROR; + } + } + + /* #impl-aarch64.ShiftReg.3 */ + public static Bits ShiftReg(int N, int reg, ShiftType type, int amount) + { + Bits result = X(N, reg); + + switch (type) + { + default: + case ShiftType.ShiftType_LSL: + result = LSL(result, amount); + break; + case ShiftType.ShiftType_LSR: + result = LSR(result, amount); + break; + case ShiftType.ShiftType_ASR: + result = ASR(result, amount); + break; + case ShiftType.ShiftType_ROR: + result = ROR(result, amount); + break; + } + + return result; + } + + // #ShiftType + public enum ShiftType {ShiftType_LSL, ShiftType_LSR, ShiftType_ASR, ShiftType_ROR}; +#endregion + } + + internal static class Shared + { + static Shared() + { + _R = new Bits[31]; + for (int i = 0; i <= 30; i++) + { + _R[i] = new Bits(64, false); + } + + SP_EL0 = new Bits(64, false); + SP_EL1 = new Bits(64, false); + + PSTATE.N = false; + PSTATE.Z = false; + PSTATE.C = false; + PSTATE.V = false; + PSTATE.EL = EL1; + PSTATE.SP = true; + } + +#region "functions/common/" + /* */ + public static Bits AND(Bits x, Bits y) + { + return x.And(y); + } + + // #impl-shared.ASR.2 + public static Bits ASR(Bits x, int shift) + { + int N = x.Count; + + /* assert shift >= 0; */ + + Bits result; + + if (shift == 0) + { + result = new Bits(x); + } + else + { + (result, _) = ASR_C(x, shift); + } + + return result; + } + + // #impl-shared.ASR_C.2 + public static (Bits, bool) ASR_C(Bits x, int shift) + { + int N = x.Count; + + /* assert shift > 0; */ + + Bits extended_x = SignExtend(x, shift + N); + Bits result = extended_x[shift + N - 1, shift]; + bool carry_out = extended_x[shift - 1]; + + return (result, carry_out); + } + + // #impl-shared.CountLeadingSignBits.1 + public static int CountLeadingSignBits(Bits x) + { + int N = x.Count; + + return CountLeadingZeroBits(EOR(x[N - 1, 1], x[N - 2, 0])); + } + + // #impl-shared.CountLeadingZeroBits.1 + public static int CountLeadingZeroBits(Bits x) + { + int N = x.Count; + + return (N - 1 - HighestSetBit(x)); + } + + /* */ + public static Bits EOR(Bits x, Bits y) + { + return x.Xor(y); + } + + // #impl-shared.Extend.3 + public static Bits Extend(Bits x, int N, bool unsigned) + { + if (unsigned) + { + return ZeroExtend(x, N); + } + else + { + return SignExtend(x, N); + } + } + + /* #impl-shared.Extend.2 */ + public static Bits Extend(int N, Bits x, bool unsigned) + { + return Extend(x, N, unsigned); + } + + // #impl-shared.HighestSetBit.1 + public static int HighestSetBit(Bits x) + { + int N = x.Count; + + for (int i = N - 1; i >= 0; i--) + { + if (x[i]) + { + return i; + } + } + + return -1; + } + + // #impl-shared.Int.2 + public static BigInteger Int(Bits x, bool unsigned) + { + return (unsigned ? UInt(x) : SInt(x)); + } + + // #impl-shared.IsOnes.1 + public static bool IsOnes(Bits x) + { + int N = x.Count; + + return (x == Ones(N)); + } + + // #impl-shared.IsZero.1 + public static bool IsZero(Bits x) + { + int N = x.Count; + + return (x == Zeros(N)); + } + + // #impl-shared.IsZeroBit.1 + public static bool IsZeroBit(Bits x) + { + return IsZero(x); + } + + // #impl-shared.LSL.2 + public static Bits LSL(Bits x, int shift) + { + int N = x.Count; + + /* assert shift >= 0; */ + + Bits result; + + if (shift == 0) + { + result = new Bits(x); + } + else + { + (result, _) = LSL_C(x, shift); + } + + return result; + } + + // #impl-shared.LSL_C.2 + public static (Bits, bool) LSL_C(Bits x, int shift) + { + int N = x.Count; + + /* assert shift > 0; */ + + Bits extended_x = Bits.Concat(x, Zeros(shift)); + Bits result = extended_x[N - 1, 0]; + bool carry_out = extended_x[N]; + + return (result, carry_out); + } + + // #impl-shared.LSR.2 + public static Bits LSR(Bits x, int shift) + { + int N = x.Count; + + /* assert shift >= 0; */ + + Bits result; + + if (shift == 0) + { + result = new Bits(x); + } + else + { + (result, _) = LSR_C(x, shift); + } + + return result; + } + + // #impl-shared.LSR_C.2 + public static (Bits, bool) LSR_C(Bits x, int shift) + { + int N = x.Count; + + /* assert shift > 0; */ + + Bits extended_x = ZeroExtend(x, shift + N); + Bits result = extended_x[shift + N - 1, shift]; + bool carry_out = extended_x[shift - 1]; + + return (result, carry_out); + } + + // #impl-shared.Min.2 + public static int Min(int a, int b) + { + if (a <= b) + { + return a; + } + else + { + return b; + } + } + + /* #impl-shared.NOT.1 */ + public static Bits NOT(Bits x) + { + return x.Not(); + } + + // #impl-shared.Ones.1 + public static Bits Ones(int N) + { + return Replicate(true, N); + } + + /* */ + public static Bits OR(Bits x, Bits y) + { + return x.Or(y); + } + + /* */ + public static decimal Real(BigInteger value) + { + return (decimal)value; + } + + // #impl-shared.ROR.2 + public static Bits ROR(Bits x, int shift) + { + /* assert shift >= 0; */ + + Bits result; + + if (shift == 0) + { + result = new Bits(x); + } + else + { + (result, _) = ROR_C(x, shift); + } + + return result; + } + + // #impl-shared.ROR_C.2 + public static (Bits, bool) ROR_C(Bits x, int shift) + { + int N = x.Count; + + /* assert shift != 0; */ + + int m = shift % N; + Bits result = OR(LSR(x, m), LSL(x, N - m)); + bool carry_out = result[N - 1]; + + return (result, carry_out); + } + + /* #impl-shared.Replicate.1 */ + public static Bits Replicate(int N, Bits x) + { + int M = x.Count; + + /* assert N MOD M == 0; */ + + return Replicate(x, N / M); + } + + /* #impl-shared.Replicate.2 */ + public static Bits Replicate(Bits x, int N) + { + int M = x.Count; + + bool[] dst = new bool[M * N]; + + for (int i = 0; i < N; i++) + { + x.CopyTo(dst, i * M); + } + + return new Bits(dst); + } + + /* #impl-shared.RoundDown.1 */ + public static BigInteger RoundDown(decimal x) + { + return (BigInteger)Decimal.Floor(x); + } + + // #impl-shared.RoundTowardsZero.1 + public static BigInteger RoundTowardsZero(decimal x) + { + if (x == 0.0m) + { + return (BigInteger)0m; + } + else if (x >= 0.0m) + { + return RoundDown(x); + } + else + { + return RoundUp(x); + } + } + + /* #impl-shared.RoundUp.1 */ + public static BigInteger RoundUp(decimal x) + { + return (BigInteger)Decimal.Ceiling(x); + } + + // #impl-shared.SInt.1 + public static BigInteger SInt(Bits x) + { + int N = x.Count; + + BigInteger result = 0; + + for (int i = 0; i <= N - 1; i++) + { + if (x[i]) + { + result = result + BigInteger.Pow(2, i); + } + } + + if (x[N - 1]) + { + result = result - BigInteger.Pow(2, N); + } + + return result; + } + + // #impl-shared.SignExtend.2 + public static Bits SignExtend(Bits x, int N) + { + int M = x.Count; + + /* assert N >= M; */ + + return Bits.Concat(Replicate(x[M - 1], N - M), x); + } + + /* #impl-shared.SignExtend.1 */ + public static Bits SignExtend(int N, Bits x) + { + return SignExtend(x, N); + } + + // #impl-shared.UInt.1 + public static BigInteger UInt(Bits x) + { + int N = x.Count; + + BigInteger result = 0; + + for (int i = 0; i <= N - 1; i++) + { + if (x[i]) + { + result = result + BigInteger.Pow(2, i); + } + } + + return result; + } + + // #impl-shared.ZeroExtend.2 + public static Bits ZeroExtend(Bits x, int N) + { + int M = x.Count; + + /* assert N >= M; */ + + return Bits.Concat(Zeros(N - M), x); + } + + /* #impl-shared.ZeroExtend.1 */ + public static Bits ZeroExtend(int N, Bits x) + { + return ZeroExtend(x, N); + } + + // #impl-shared.Zeros.1 + /* #impl-shared.Zeros.0 */ + public static Bits Zeros(int N) + { + return Replicate(false, N); + } +#endregion + +#region "functions/crc/" + // #impl-shared.BitReverse.1 + public static Bits BitReverse(Bits data) + { + int N = data.Count; + + Bits result = new Bits(N); + + for (int i = 0; i <= N - 1; i++) + { + result[N - i - 1] = data[i]; + } + + return result; + } + + // #impl-shared.Poly32Mod2.2 + public static Bits Poly32Mod2(Bits _data, Bits poly) + { + int N = _data.Count; + + /* assert N > 32; */ + + Bits data = new Bits(_data); + + for (int i = N - 1; i >= 32; i--) + { + if (data[i]) + { + data[i - 1, 0] = EOR(data[i - 1, 0], Bits.Concat(poly, Zeros(i - 32))); + } + } + + return data[31, 0]; + } +#endregion + +#region "functions/integer/" + /* #impl-shared.AddWithCarry.3 */ + public static (Bits, Bits) AddWithCarry(int N, Bits x, Bits y, bool carry_in) + { + BigInteger unsigned_sum = UInt(x) + UInt(y) + UInt(carry_in); + BigInteger signed_sum = SInt(x) + SInt(y) + UInt(carry_in); + + Bits result = unsigned_sum.SubBigInteger(N - 1, 0); // same value as signed_sum<N-1:0> + + bool n = result[N - 1]; + bool z = IsZero(result); + bool c = !(UInt(result) == unsigned_sum); + bool v = !(SInt(result) == signed_sum); + + return (result, Bits.Concat(n, z, c, v)); + } +#endregion + +#region "functions/registers/" + public static readonly Bits[] _R; + + public static Bits SP_EL0; + public static Bits SP_EL1; +#endregion + +#region "functions/system/" + // #impl-shared.ConditionHolds.1 + public static bool ConditionHolds(Bits cond) + { + bool result; + + // Evaluate base condition. + switch (cond[3, 1]) + { + case Bits bits when bits == "000": + result = (PSTATE.Z == true); // EQ or NE + break; + case Bits bits when bits == "001": + result = (PSTATE.C == true); // CS or CC + break; + case Bits bits when bits == "010": + result = (PSTATE.N == true); // MI or PL + break; + case Bits bits when bits == "011": + result = (PSTATE.V == true); // VS or VC + break; + case Bits bits when bits == "100": + result = (PSTATE.C == true && PSTATE.Z == false); // HI or LS + break; + case Bits bits when bits == "101": + result = (PSTATE.N == PSTATE.V); // GE or LT + break; + case Bits bits when bits == "110": + result = (PSTATE.N == PSTATE.V && PSTATE.Z == false); // GT or LE + break; + default: + case Bits bits when bits == "111": + result = true; // AL + break; + } + + // Condition flag values in the set '111x' indicate always true + // Otherwise, invert condition if necessary. + if (cond[0] == true && cond != "1111") + { + result = !result; + } + + return result; + } + + // #EL3 + public static readonly Bits EL3 = "11"; + // #EL2 + public static readonly Bits EL2 = "10"; + // #EL1 + public static readonly Bits EL1 = "01"; + // #EL0 + public static readonly Bits EL0 = "00"; + + /* #impl-shared.HaveEL.1 */ + public static bool HaveEL(Bits el) + { + if (el == EL1 || el == EL0) + { + return true; // EL1 and EL0 must exist + } + + return false; + } + + public static ProcState PSTATE; + + /* #ProcState */ + internal struct ProcState + { + public void NZCV(Bits nzcv) // ASL: ".<,,,>". + { + N = nzcv[3]; + Z = nzcv[2]; + C = nzcv[1]; + V = nzcv[0]; + } + + public void NZCV(bool n, bool z, bool c, bool v) // ASL: ".<,,,>". + { + N = n; + Z = z; + C = c; + V = v; + } + + public bool N; // Negative condition flag + public bool Z; // Zero condition flag + public bool C; // Carry condition flag + public bool V; // oVerflow condition flag + public Bits EL; // Exception Level + public bool SP; // Stack pointer select: 0=SP0, 1=SPx [AArch64 only] + } +#endregion + } +} diff --git a/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs b/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs new file mode 100644 index 00000000..f4ee966c --- /dev/null +++ b/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs @@ -0,0 +1,248 @@ +// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Types/Bits.cs + +// https://github.com/dotnet/corefx/blob/master/src/System.Collections/src/System/Collections/BitArray.cs + +using System; +using System.Collections; +using System.Numerics; + +namespace Ryujinx.Tests.Cpu.Tester.Types +{ + internal sealed class Bits : ICollection, IEnumerable, IEquatable<Bits> + { + private BitArray bits; + + public Bits(bool[] values) => bits = new BitArray(values); + public Bits(byte[] bytes) => bits = new BitArray(bytes); + public Bits(Bits bits) => this.bits = new BitArray(bits.bits); + private Bits(BitArray bitArray) => bits = new BitArray(bitArray); + public Bits(int length) => bits = new BitArray(length); + public Bits(int length, bool defaultValue) => bits = new BitArray(length, defaultValue); + public Bits(ulong value) => bits = new BitArray(BitConverter.GetBytes(value)); + public Bits(uint value) => bits = new BitArray(BitConverter.GetBytes(value)); + public Bits(ushort value) => bits = new BitArray(BitConverter.GetBytes(value)); + public Bits(byte value) => bits = new BitArray(new byte[1] {value}); + + public ulong ToUInt64() + { + byte[] dst = new byte[8]; + + bits.CopyTo(dst, 0); + + return BitConverter.ToUInt64(dst, 0); + } + public uint ToUInt32() + { + byte[] dst = new byte[4]; + + bits.CopyTo(dst, 0); + + return BitConverter.ToUInt32(dst, 0); + } + private BitArray ToBitArray() => new BitArray(bits); + + public bool this[int index] // ASL: "<>". + { + get + { + return bits.Get(index); + } + set + { + bits.Set(index, value); + } + } + public Bits this[int highIndex, int lowIndex] // ASL: "<:>". + { + get + { + if (highIndex < lowIndex) + { + throw new IndexOutOfRangeException(); + } + + bool[] dst = new bool[highIndex - lowIndex + 1]; + + for (int i = lowIndex, n = 0; i <= highIndex; i++, n++) + { + dst[n] = bits.Get(i); + } + + return new Bits(dst); + } + set + { + if (highIndex < lowIndex) + { + throw new IndexOutOfRangeException(); + } + + for (int i = lowIndex, n = 0; i <= highIndex; i++, n++) + { + bits.Set(i, value.Get(n)); + } + } + } + + public bool IsReadOnly { get => false; } // Mutable. + public int Count { get => bits.Count; } + public bool IsSynchronized { get => bits.IsSynchronized; } + public object SyncRoot { get => bits.SyncRoot; } + public Bits And(Bits value) => new Bits(new BitArray(this.bits).And(value.bits)); // Immutable. + public void CopyTo(Array array, int index) => bits.CopyTo(array, index); + public bool Get(int index) => bits.Get(index); + public IEnumerator GetEnumerator() => bits.GetEnumerator(); + //public Bits LeftShift(int count) => new Bits(new BitArray(bits).LeftShift(count)); // Immutable. + public Bits Not() => new Bits(new BitArray(bits).Not()); // Immutable. + public Bits Or(Bits value) => new Bits(new BitArray(this.bits).Or(value.bits)); // Immutable. + //public Bits RightShift(int count) => new Bits(new BitArray(bits).RightShift(count)); // Immutable. + public void Set(int index, bool value) => bits.Set(index, value); + public void SetAll(bool value) => bits.SetAll(value); + public Bits Xor(Bits value) => new Bits(new BitArray(this.bits).Xor(value.bits)); // Immutable. + + public static Bits Concat(Bits highBits, Bits lowBits) // ASL: ":". + { + if (((object)lowBits == null) || ((object)highBits == null)) + { + throw new ArgumentNullException(); + } + + bool[] dst = new bool[lowBits.Count + highBits.Count]; + + lowBits.CopyTo(dst, 0); + highBits.CopyTo(dst, lowBits.Count); + + return new Bits(dst); + } + public static Bits Concat(bool bit3, bool bit2, bool bit1, bool bit0) // ASL: ":::". + { + return new Bits(new bool[] {bit0, bit1, bit2, bit3}); + } + + public static implicit operator Bits(bool value) => new Bits(1, value); + public static implicit operator Bits(string value) + { + if (String.IsNullOrEmpty(value)) + { + throw new InvalidCastException(); + } + + bool[] dst = new bool[value.Length]; + + for (int i = value.Length - 1, n = 0; i >= 0; i--, n++) + { + if (value[i] == '1') + { + dst[n] = true; + } + else if (value[i] == '0') + { + dst[n] = false; + } + else + { + throw new InvalidCastException(); + } + } + + return new Bits(dst); + } + public static explicit operator bool(Bits bit) + { + if (((object)bit == null) || (bit.Count != 1)) + { + throw new InvalidCastException(); + } + + return bit.Get(0); + } + + public static Bits operator +(Bits left, BigInteger right) // ASL: "+". + { + if (((object)left == null) || ((object)right == null)) + { + throw new ArgumentNullException(); + } + + BigInteger dst; + + if (left.Count <= 32) + { + dst = left.ToUInt32() + right; + } + else if (left.Count <= 64) + { + dst = left.ToUInt64() + right; + } + else + { + throw new ArgumentOutOfRangeException(); + } + + return dst.SubBigInteger(left.Count - 1, 0); + } + public static bool operator ==(Bits left, Bits right) // ASL: "==". + { + if (((object)left == null) || ((object)right == null)) + { + throw new ArgumentNullException(); + } + + if (left.Count != right.Count) + { + return false; + } + + for (int i = 0; i <= left.Count - 1; i++) + { + if (left.Get(i) != right.Get(i)) + { + return false; + } + } + + return true; + } + public static bool operator !=(Bits left, Bits right) // ASL: "!=". + { + return !(left == right); + } + + public bool Equals(Bits right) // ASL: "==". + { + if ((object)right == null) + { + throw new ArgumentNullException(); + } + + Bits left = this; + + if (left.Count != right.Count) + { + return false; + } + + for (int i = 0; i <= left.Count - 1; i++) + { + if (left.Get(i) != right.Get(i)) + { + return false; + } + } + + return true; + } + public override bool Equals(object obj) + { + if (obj == null) + { + throw new ArgumentNullException(); + } + + Bits right = obj as Bits; + + return Equals(right); + } + public override int GetHashCode() => bits.GetHashCode(); + } +} diff --git a/Ryujinx.Tests/Cpu/Tester/Types/Integer.cs b/Ryujinx.Tests/Cpu/Tester/Types/Integer.cs new file mode 100644 index 00000000..c72f3e25 --- /dev/null +++ b/Ryujinx.Tests/Cpu/Tester/Types/Integer.cs @@ -0,0 +1,42 @@ +// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Types/Integer.cs + +using System; +using System.Numerics; + +namespace Ryujinx.Tests.Cpu.Tester.Types +{ + internal static class Integer + { + public static Bits SubBigInteger(this BigInteger x, int highIndex, int lowIndex) // ASL: "<:>". + { + if (highIndex < lowIndex) + { + throw new IndexOutOfRangeException(); + } + + Bits src = new Bits(x.ToByteArray()); + bool[] dst = new bool[highIndex - lowIndex + 1]; + + for (int i = lowIndex, n = 0; i <= highIndex; i++, n++) + { + if (i <= src.Count - 1) + { + dst[n] = src[i]; + } + else + { + dst[n] = (x.Sign != -1 ? false : true); // Zero / Sign Extension. + } + } + + return new Bits(dst); + } + + public static bool SubBigInteger(this BigInteger x, int index) // ASL: "<>". + { + Bits dst = x.SubBigInteger(index, index); + + return (bool)dst; + } + } +} |
