diff --git a/Disarm/Arm64Instruction.cs b/Disarm/Arm64Instruction.cs index efb0b01..c42bce0 100644 --- a/Disarm/Arm64Instruction.cs +++ b/Disarm/Arm64Instruction.cs @@ -147,13 +147,13 @@ public override string ToString() sb.Append(' '); //Ew yes I'm using goto. - if (!AppendOperand(sb, Op0Kind, Op0Reg, Op0VectorElement, Op0Arrangement, Op1ShiftType, Op0Imm, Op0FpImm)) + if (!AppendOperand(sb, Op0Kind, Op0Reg, Op0VectorElement, Op0Arrangement, Op0ShiftType, Op0Imm, Op0FpImm, false, MemExtendOrShiftAmount)) goto doneops; - if (!AppendOperand(sb, Op1Kind, Op1Reg, Op1VectorElement, Op1Arrangement, Op1ShiftType, Op1Imm, Op1FpImm, true)) + if (!AppendOperand(sb, Op1Kind, Op1Reg, Op1VectorElement, Op1Arrangement, Op1ShiftType, Op1Imm, Op1FpImm, true, MemExtendOrShiftAmount)) goto doneops; - if (!AppendOperand(sb, Op2Kind, Op2Reg, Op2VectorElement, Op2Arrangement, Op1ShiftType, Op2Imm, Op2FpImm, true)) + if (!AppendOperand(sb, Op2Kind, Op2Reg, Op2VectorElement, Op2Arrangement, Op2ShiftType, Op2Imm, Op2FpImm, true, MemExtendOrShiftAmount)) goto doneops; - if (!AppendOperand(sb, Op3Kind, Op3Reg, Op3VectorElement, Op3Arrangement, Op1ShiftType, Op3Imm, Op3FpImm, true)) + if (!AppendOperand(sb, Op3Kind, Op3Reg, Op3VectorElement, Op3Arrangement, Op3ShiftType, Op3Imm, Op3FpImm, true, MemExtendOrShiftAmount)) goto doneops; doneops: @@ -167,7 +167,7 @@ public override string ToString() return sb.ToString(); } - private bool AppendOperand(StringBuilder sb, Arm64OperandKind kind, Arm64Register reg, Arm64VectorElement vectorElement, Arm64ArrangementSpecifier regArrangement, Arm64ShiftType shiftType, long imm, double fpImm, bool comma = false) + private bool AppendOperand(StringBuilder sb, Arm64OperandKind kind, Arm64Register reg, Arm64VectorElement vectorElement, Arm64ArrangementSpecifier regArrangement, Arm64ShiftType shiftType, long imm, double fpImm, bool comma = false, int shiftAmount = 0) { if (kind == Arm64OperandKind.None) return false; @@ -189,12 +189,14 @@ private bool AppendOperand(StringBuilder sb, Arm64OperandKind kind, Arm64Registe } else if (kind == Arm64OperandKind.Immediate) { + sb.Append("#0x").Append(imm.ToString("X")); if (shiftType != Arm64ShiftType.NONE) - sb.Append(shiftType).Append(' '); - sb.Append("0x").Append(imm.ToString("X")); + sb.Append(",").Append(shiftType).Append("#").Append(shiftAmount); } else if (kind == Arm64OperandKind.FloatingPointImmediate) { + sb.Append("#"); sb.Append(fpImm.ToString(CultureInfo.InvariantCulture)); + } else if(kind == Arm64OperandKind.ImmediatePcRelative) sb.Append("0x").Append(((long) Address + imm).ToString("X")); diff --git a/Disarm/Arm64Mnemonic.cs b/Disarm/Arm64Mnemonic.cs index 4232e0a..4b59a7f 100644 --- a/Disarm/Arm64Mnemonic.cs +++ b/Disarm/Arm64Mnemonic.cs @@ -102,6 +102,7 @@ public enum Arm64Mnemonic CLREX, CLS, CLZ, + CNT, CMN, CMP, CMPP, @@ -139,6 +140,7 @@ public enum Arm64Mnemonic ERETAB, ESB, EXTR, + EXT, // Advanced SIMD extract FABD, FABS, FACGE, @@ -552,6 +554,8 @@ public enum Arm64Mnemonic TLBI, TSB_CSYNC, TST, + TRN1, + TRN2, RSUBHN, RSUBHN2, SABA, SABAL, SABAL2, @@ -712,13 +716,18 @@ public enum Arm64Mnemonic UMULL, UXTB, UXTH, + UZP1, + UZP2, WFE, WFET, WFI, WFIT, XAFLAG, + XTN, XPACD,XPACI,XPACLRI, YIELD, + ZIP1, + ZIP2, CMHL, CMHS, CMHI, diff --git a/Disarm/InternalDisassembly/Arm64Aliases.cs b/Disarm/InternalDisassembly/Arm64Aliases.cs index e608472..efcbd0e 100644 --- a/Disarm/InternalDisassembly/Arm64Aliases.cs +++ b/Disarm/InternalDisassembly/Arm64Aliases.cs @@ -25,6 +25,24 @@ public static void CheckForAlias(ref Arm64Instruction instruction) return; } + // ORR Xd, XZR, #imm => MOV Xd, #imm + if (instruction.Mnemonic == Arm64Mnemonic.ORR && instruction.Op1Reg is Arm64Register.X31 or Arm64Register.W31 && instruction.Op2Kind == Arm64OperandKind.Immediate) + { + instruction.Mnemonic = Arm64Mnemonic.MOV; + + // Move immediate to Op1 + instruction.Op1Kind = Arm64OperandKind.Immediate; + instruction.Op1Imm = instruction.Op2Imm; + + // Clear Op2 + instruction.Op2Kind = Arm64OperandKind.None; + instruction.Op2Imm = 0; + + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + + return; + } + if (instruction.Mnemonic == Arm64Mnemonic.ORR && instruction.Op1Kind == Arm64OperandKind.Register && instruction.Op2Kind == Arm64OperandKind.Register && instruction.Op1Reg == instruction.Op2Reg) { //Change ORR R0, R1, R1 => MOV R0, R1 @@ -60,7 +78,7 @@ public static void CheckForAlias(ref Arm64Instruction instruction) return; } - + if (instruction.Mnemonic == Arm64Mnemonic.MADD && instruction.Op3Reg is Arm64Register.X31 or Arm64Register.W31) { //MADD Rd, Rn, Rm, ZR => MUL Rd, Rn, Rm @@ -76,6 +94,64 @@ public static void CheckForAlias(ref Arm64Instruction instruction) return; } + // SMADDL Rd, Rn, Rm, X31 => SMULL Rd, Wn, Wm + // According to ARM64 specification, when the accumulator is X31 (zero register), + // SMADDL should be aliased to SMULL with 32-bit source operands + if (instruction.Mnemonic == Arm64Mnemonic.SMADDL && instruction.Op3Reg == Arm64Register.X31) + { + instruction.Mnemonic = Arm64Mnemonic.SMULL; + + // Convert source registers from X to W (e.g., X8 -> W8, X25 -> W25) + // The destination stays as X register + var regN = instruction.Op1Reg; + var regM = instruction.Op2Reg; + + if (regN >= Arm64Register.X0 && regN <= Arm64Register.X30) + { + instruction.Op1Reg = Arm64Register.W0 + (regN - Arm64Register.X0); + } + + if (regM >= Arm64Register.X0 && regM <= Arm64Register.X30) + { + instruction.Op2Reg = Arm64Register.W0 + (regM - Arm64Register.X0); + } + + // Clear the accumulator operand + instruction.Op3Kind = Arm64OperandKind.None; + instruction.Op3Reg = Arm64Register.INVALID; + + return; + } + + // UMADDL Rd, Rn, Rm, X31 => UMULL Rd, Wn, Wm + // According to ARM64 specification, when the accumulator is X31 (zero register), + // UMADDL should be aliased to UMULL with 32-bit source operands + if (instruction.Mnemonic == Arm64Mnemonic.UMADDL && instruction.Op3Reg == Arm64Register.X31) + { + instruction.Mnemonic = Arm64Mnemonic.UMULL; + + // Convert source registers from X to W (e.g., X26 -> W26, X9 -> W9) + // The destination stays as X register + var regN = instruction.Op1Reg; + var regM = instruction.Op2Reg; + + if (regN is >= Arm64Register.X0 and <= Arm64Register.X30) + { + instruction.Op1Reg = Arm64Register.W0 + (regN - Arm64Register.X0); + } + + if (regM is >= Arm64Register.X0 and <= Arm64Register.X30) + { + instruction.Op2Reg = Arm64Register.W0 + (regM - Arm64Register.X0); + } + + // Clear the accumulator operand + instruction.Op3Kind = Arm64OperandKind.None; + instruction.Op3Reg = Arm64Register.INVALID; + + return; + } + if (instruction.Mnemonic == Arm64Mnemonic.CSINC && instruction.FinalOpConditionCode is not Arm64ConditionCode.AL and not Arm64ConditionCode.NV && instruction.Op2Kind == Arm64OperandKind.Register && instruction.Op1Kind == Arm64OperandKind.Register) { if(instruction.Op2Reg.IsSp() && instruction.Op1Reg.IsSp()) @@ -101,6 +177,20 @@ public static void CheckForAlias(ref Arm64Instruction instruction) } + // CSNEG Rd, Rn, Rm, cond => CNEG Rd, Rn, !cond when Rn == Rm + if (instruction.Mnemonic == Arm64Mnemonic.CSNEG && instruction.FinalOpConditionCode is not Arm64ConditionCode.AL and not Arm64ConditionCode.NV && instruction.Op2Kind == Arm64OperandKind.Register && instruction.Op1Kind == Arm64OperandKind.Register) + { + if(!instruction.Op2Reg.IsSp() && !instruction.Op1Reg.IsSp() && instruction.Op1Reg == instruction.Op2Reg) + { + //CSNEG Rd, Rn, Rn, COND => CNEG Rd, Rn, !COND + instruction.FinalOpConditionCode = instruction.FinalOpConditionCode.Invert(); + instruction.Op2Kind = Arm64OperandKind.None; + instruction.Op2Reg = Arm64Register.INVALID; + instruction.Mnemonic = Arm64Mnemonic.CNEG; + return; + } + } + if (instruction.Mnemonic == Arm64Mnemonic.SBFM && instruction.Op2Kind == Arm64OperandKind.Immediate && instruction.Op3Kind == Arm64OperandKind.Immediate && instruction.Op2Imm == 0) { //Check imm3 @@ -130,22 +220,183 @@ public static void CheckForAlias(ref Arm64Instruction instruction) return; } - - if (instruction.Mnemonic == Arm64Mnemonic.INS && instruction.Op0Kind == Arm64OperandKind.VectorRegisterElement && instruction.Op1Kind == Arm64OperandKind.VectorRegisterElement) + + // UBFM to LSL alias conversion + if (instruction.Mnemonic == Arm64Mnemonic.UBFM && instruction.Op2Kind == Arm64OperandKind.Immediate && instruction.Op3Kind == Arm64OperandKind.Immediate) { - //INS Vd.Ts[i1], Vn.Ts[i2] => MOV Vd.Ts[i1], Vn.Ts[i2] - //i.e. just change INS to MOV - instruction.Mnemonic = Arm64Mnemonic.MOV; + var immr = instruction.Op2Imm; + var imms = instruction.Op3Imm; + var is64Bit = instruction.Op0Reg >= Arm64Register.X0 && instruction.Op0Reg <= Arm64Register.X31; + var regWidth = is64Bit ? 64 : 32; + + // Check if this matches LSL pattern: UBFM Rd, Rn, #(-shift MOD width), #(width-1-shift) + // For LSL: immr = (-shift) MOD width, imms = (width-1-shift) + // So: shift = (width - immr) MOD width, and imms should equal (width-1-shift) + var shift = (regWidth - immr) % regWidth; + if (imms == regWidth - 1 - shift) + { + // Convert to LSL + instruction.Mnemonic = Arm64Mnemonic.LSL; + instruction.Op2Imm = shift; + instruction.Op3Kind = Arm64OperandKind.None; + instruction.Op3Imm = 0; + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + + // Check if this matches LSR pattern: UBFM Rd, Rn, #shift, #(width-1) + if (imms == regWidth - 1) + { + // Convert to LSR + instruction.Mnemonic = Arm64Mnemonic.LSR; + instruction.Op2Imm = immr; + instruction.Op3Kind = Arm64OperandKind.None; + instruction.Op3Imm = 0; + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + + // Check if this matches UBFIZ pattern: UBFM Rd, Rn, #(-lsb MOD width), #(width-lsb-1) + // where lsb is the least significant bit position and width is the field width + var lsb = (regWidth - immr) % regWidth; + var fieldWidth = imms + 1; + if (lsb + fieldWidth <= regWidth && immr == (regWidth - lsb) % regWidth && imms == fieldWidth - 1) + { + // Convert to UBFIZ + instruction.Mnemonic = Arm64Mnemonic.UBFIZ; + instruction.Op2Imm = lsb; + instruction.Op3Imm = fieldWidth; + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + + // Check if this matches UBFX pattern: UBFM Rd, Rn, #lsb, #(lsb+width-1) + if (immr <= imms) + { + var ubfxWidth = imms - immr + 1; + // Convert to UBFX + instruction.Mnemonic = Arm64Mnemonic.UBFX; + instruction.Op2Imm = immr; // lsb + instruction.Op3Imm = ubfxWidth; // width + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + } + + // SBFM to ASR/SBFIZ/SBFX alias conversion + if (instruction.Mnemonic == Arm64Mnemonic.SBFM && instruction.Op2Kind == Arm64OperandKind.Immediate && instruction.Op3Kind == Arm64OperandKind.Immediate) + { + var immr = instruction.Op2Imm; + var imms = instruction.Op3Imm; + var is64Bit = instruction.Op0Reg >= Arm64Register.X0 && instruction.Op0Reg <= Arm64Register.X31; + var regWidth = is64Bit ? 64 : 32; + + // Check if this matches ASR pattern: SBFM Rd, Rn, #shift, #(width-1) + if (imms == regWidth - 1) + { + // Convert to ASR + instruction.Mnemonic = Arm64Mnemonic.ASR; + instruction.Op2Imm = immr; + instruction.Op3Kind = Arm64OperandKind.None; + instruction.Op3Imm = 0; + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + + // Check if this matches SBFIZ pattern: SBFM Rd, Rn, #(-lsb MOD width), #(width-lsb-1) + var lsb = (regWidth - immr) % regWidth; + var fieldWidth = imms + 1; + if (lsb + fieldWidth <= regWidth && immr == (regWidth - lsb) % regWidth && imms == fieldWidth - 1) + { + // Convert to SBFIZ + instruction.Mnemonic = Arm64Mnemonic.SBFIZ; + instruction.Op2Imm = lsb; + instruction.Op3Imm = fieldWidth; + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + + // Check if this matches SBFX pattern: SBFM Rd, Rn, #lsb, #(lsb+width-1) + if (immr <= imms) + { + var sbfxWidth = imms - immr + 1; + // Convert to SBFX + instruction.Mnemonic = Arm64Mnemonic.SBFX; + instruction.Op2Imm = immr; // lsb + instruction.Op3Imm = sbfxWidth; // width + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + } + + // BFM to BFI alias conversion + if (instruction.Mnemonic == Arm64Mnemonic.BFM && instruction.Op2Kind == Arm64OperandKind.Immediate && instruction.Op3Kind == Arm64OperandKind.Immediate) + { + var immr = instruction.Op2Imm; + var imms = instruction.Op3Imm; + var is64Bit = instruction.Op0Reg >= Arm64Register.X0 && instruction.Op0Reg <= Arm64Register.X31; + var regWidth = is64Bit ? 64 : 32; + + // Check if this matches BFXIL pattern: BFM Rd, Rn, #lsb, #(lsb+width-1) + // where lsb is the extraction start position and width is the field width + if (immr <= imms) + { + var lsb = immr; + var fieldWidth = imms - immr + 1; + + if (lsb + fieldWidth <= regWidth) + { + // Convert to BFXIL + instruction.Mnemonic = Arm64Mnemonic.BFXIL; + instruction.Op2Imm = lsb; + instruction.Op3Imm = fieldWidth; + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + } + + // Check if this matches BFI pattern: BFM Rd, Rn, #(-lsb MOD width), #(width-1) + // where lsb is the insertion position and width is the field width + var bfiLsb = (regWidth - immr) % regWidth; + var bfiFieldWidth = imms + 1; - //Category remains SimdRegisterToRegister + if (bfiLsb + bfiFieldWidth <= regWidth && immr == (regWidth - bfiLsb) % regWidth && imms == bfiFieldWidth - 1) + { + // Convert to BFI + instruction.Mnemonic = Arm64Mnemonic.BFI; + instruction.Op2Imm = bfiLsb; + instruction.Op3Imm = bfiFieldWidth; + instruction.MnemonicCategory = Arm64MnemonicCategory.Move; + return; + } + } + + // Variable shift instructions to their more readable aliases + // LSLV Rd, Rn, Rm => LSL Rd, Rn, Rm + if (instruction.Mnemonic == Arm64Mnemonic.LSLV) + { + instruction.Mnemonic = Arm64Mnemonic.LSL; return; } - - if (instruction.Mnemonic == Arm64Mnemonic.DUP && instruction.Op0Kind == Arm64OperandKind.Register && instruction.Op1Kind == Arm64OperandKind.VectorRegisterElement) + + // LSRV Rd, Rn, Rm => LSR Rd, Rn, Rm + if (instruction.Mnemonic == Arm64Mnemonic.LSRV) { - //DUP Rd, Vn.Ts[i] => MOV Rd, Vn.Ts[i] - //i.e. just change DUP to MOV - instruction.Mnemonic = Arm64Mnemonic.MOV; + instruction.Mnemonic = Arm64Mnemonic.LSR; + return; + } + + // ASRV Rd, Rn, Rm => ASR Rd, Rn, Rm + if (instruction.Mnemonic == Arm64Mnemonic.ASRV) + { + instruction.Mnemonic = Arm64Mnemonic.ASR; + return; + } + + // RORV Rd, Rn, Rm => ROR Rd, Rn, Rm + if (instruction.Mnemonic == Arm64Mnemonic.RORV) + { + instruction.Mnemonic = Arm64Mnemonic.ROR; return; } } diff --git a/Disarm/InternalDisassembly/Arm64Branches.cs b/Disarm/InternalDisassembly/Arm64Branches.cs index 5cee7fe..373727f 100644 --- a/Disarm/InternalDisassembly/Arm64Branches.cs +++ b/Disarm/InternalDisassembly/Arm64Branches.cs @@ -52,12 +52,16 @@ public static Arm64Instruction TestAndBranch(uint instruction) var mnemonic = isNegated ? Arm64Mnemonic.TBNZ : Arm64Mnemonic.TBZ; + // 计算要测试的位号:b5位决定是32位还是64位,b40是位号的高5位 var bitToTest = b40; if (b5) - bitToTest &= 1 << 5; + bitToTest |= 32; // 对于64位寄存器,b5=1时位号需要加上32 var jumpTo = Arm64CommonUtils.CorrectSignBit(imm14, 14) * 4; - var regT = Arm64Register.X0 + rt; + + // 根据b5位决定使用32位还是64位寄存器 + var baseReg = b5 ? Arm64Register.X0 : Arm64Register.W0; + var regT = baseReg + rt; return new() { diff --git a/Disarm/InternalDisassembly/Arm64CommonUtils.cs b/Disarm/InternalDisassembly/Arm64CommonUtils.cs index a9065e5..58a28ff 100644 --- a/Disarm/InternalDisassembly/Arm64CommonUtils.cs +++ b/Disarm/InternalDisassembly/Arm64CommonUtils.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Linq; namespace Disarm.InternalDisassembly; @@ -69,14 +70,17 @@ private static long BitsToLong(BitArray bits) return result; } - private static ulong RotateRight(ulong original, int numBits, int shift) + public static ulong RotateRight(ulong original, int numBits, int shift) { var m = shift % numBits; var right = original >> m; var left = original << (numBits - m); - - return right | left; + + // 确保左移结果不会超出numBits的范围 + var mask = numBits == 64 ? ulong.MaxValue : (1UL << numBits) - 1; + + return (right | left) & mask; } private static BitArray LongToBits(long value, int numBits) @@ -155,31 +159,43 @@ public static (long, long) DecodeBitMasks(bool nFlag, int desiredSize, byte imms //imms and immr are actually 6 bits not 8. var combined = (short)((nFlag ? 1 << 6 : 0) | (~imms & 0b11_1111)); - var bits = LongToBits(combined, 12); - var len = HighestSetBit(bits); + var len = HighestSetBit(combined, 12); if (len < 1) throw new Arm64UndefinedInstructionException("DecodeBitMasks: highestBit < 1"); - if ((1 << len) > desiredSize) - throw new Arm64UndefinedInstructionException("DecodeBitMasks: (1 << highestBit) > desiredSize"); + // 计算element size + var esize = 1 << len; + + if (esize > desiredSize) + { + throw new Arm64UndefinedInstructionException("DecodeBitMasks: (1 << len) > desiredSize"); + } var levels = (1 << len) - 1; - if (immediate && (imms & levels) == levels) - throw new Arm64UndefinedInstructionException("DecodeBitMasks: imms & levels == levels not allowed in immediate mode"); - var s = imms & levels; var r = immr & levels; var diff = s - r; - var esize = 1 << len; var d = diff & ((1 << (len - 1)) - 1); //UInt(diff) - var wElem = (1 << (s + 1)) - 1; - var tElem = (1 << (d + 1)) - 1; - - var wMask = Replicate(LongToBits((long)RotateRight((ulong)wElem, esize, r), esize), desiredSize); - var tMask = Replicate(LongToBits(tElem, esize), desiredSize); + + // 计算wElem和tElem + long wElem = (1L << (s + 1)) - 1; + long tElem = (1L << (d + 1)) - 1; + + // 创建在esize范围内的位模式,然后旋转 + var maskedWElem = esize == 64 ? wElem : wElem & ((1L << esize) - 1); + + ulong rotatedWElem = RotateRight((ulong)maskedWElem, esize, (int)r); + + // 使用原始的esize位模式,而不是旋转后的64位值 + BitArray rotatedBits = LongToBits(maskedWElem, esize); // 使用原始的maskedWElem + RotateRightInPlace(rotatedBits, (int)r); // 在位数组上直接进行旋转 + + BitArray wMask = Replicate(rotatedBits, desiredSize); + + BitArray tMask = Replicate(LongToBits(tElem, esize), desiredSize); return (BitsToLong(wMask), BitsToLong(tMask)); } @@ -384,4 +400,27 @@ private static double ToFloat16(byte[] bytes) return sign == 1 ? -result : result; #endif } + + private static void RotateRightInPlace(BitArray bits, int shiftAmount) + { + int length = bits.Length; + if (length == 0 || shiftAmount == 0) return; + + shiftAmount %= length; + if (shiftAmount == 0) return; + + // 创建临时数组存储原始值 + bool[] temp = new bool[length]; + for (int i = 0; i < length; i++) + { + temp[i] = bits[i]; + } + + // 执行右旋转 + for (int i = 0; i < length; i++) + { + int newIndex = (i + shiftAmount) % length; + bits[newIndex] = temp[i]; + } + } } \ No newline at end of file diff --git a/Disarm/InternalDisassembly/Arm64DataProcessingImmediate.cs b/Disarm/InternalDisassembly/Arm64DataProcessingImmediate.cs index c7ed1a6..2c06c4d 100644 --- a/Disarm/InternalDisassembly/Arm64DataProcessingImmediate.cs +++ b/Disarm/InternalDisassembly/Arm64DataProcessingImmediate.cs @@ -206,16 +206,43 @@ public static Arm64Instruction MoveWideImmediate(uint instruction) var regD = baseReg + rd; var shift = (int) hw * 16; - - imm16 <<= shift; + + // For MOVK, we store the original immediate and shift info + // For other instructions, we calculate the shifted value + long immValue; + Arm64ShiftType shiftType = Arm64ShiftType.NONE; + int shiftAmount = 0; + if (mnemonic == Arm64Mnemonic.MOVK && shift > 0) + { + // For MOVK, store the original 16-bit immediate + immValue = (long)imm16; + shiftType = Arm64ShiftType.LSL; + shiftAmount = shift; + } + else + { + // For MOVN and MOVZ, calculate the shifted value + immValue = (long)imm16 << shift; + // If the instruction is 32-bit, we need to mask the immediate value to 32 bits + if (!is64Bit) + immValue &= 0xFFFFFFFF; + } + + if (mnemonic==Arm64Mnemonic.MOVZ) + { + mnemonic = Arm64Mnemonic.MOV; + } + return new() { Mnemonic = mnemonic, Op0Kind = Arm64OperandKind.Register, Op1Kind = Arm64OperandKind.Immediate, Op0Reg = regD, - Op1Imm = imm16, + Op1Imm = immValue, + Op1ShiftType = shiftType, + MemExtendOrShiftAmount = shiftAmount, MnemonicCategory = Arm64MnemonicCategory.Move, }; } diff --git a/Disarm/InternalDisassembly/Arm64DataProcessingRegister.cs b/Disarm/InternalDisassembly/Arm64DataProcessingRegister.cs index 7cb6962..763ac07 100644 --- a/Disarm/InternalDisassembly/Arm64DataProcessingRegister.cs +++ b/Disarm/InternalDisassembly/Arm64DataProcessingRegister.cs @@ -566,10 +566,15 @@ private static Arm64Instruction DataProcessing3Source(uint instruction) _ => throw new Arm64UndefinedInstructionException($"DataProcessing3Source: unallocated operand combination: op31 = {op31} o0 = {o0} sf = {(is64Bit ? 1 : 0)}") }; + // For SMADDL, SMSUBL, UMADDL, UMSUBL: destination and accumulator are 64-bit, sources are 32-bit + var isLongMultiply = mnemonic is Arm64Mnemonic.SMADDL or Arm64Mnemonic.SMSUBL or Arm64Mnemonic.UMADDL or Arm64Mnemonic.UMSUBL; + var baseReg = is64Bit ? Arm64Register.X0 : Arm64Register.W0; + var sourceBaseReg = (isLongMultiply && is64Bit) ? Arm64Register.W0 : baseReg; - var regM = baseReg + rm; - var regN = baseReg + rn; + var regM = sourceBaseReg + rm; + var regN = sourceBaseReg + rn; + var regD = baseReg + rd; var regA = baseReg + ra; diff --git a/Disarm/InternalDisassembly/Arm64FloatingPoint.cs b/Disarm/InternalDisassembly/Arm64FloatingPoint.cs index 8f2ffcd..170ab8b 100644 --- a/Disarm/InternalDisassembly/Arm64FloatingPoint.cs +++ b/Disarm/InternalDisassembly/Arm64FloatingPoint.cs @@ -45,6 +45,7 @@ internal static Arm64Instruction ConversionToAndFromFixedPoint(uint instruction) 0b001 => Arm64Mnemonic.FCVTZU, 0b010 => Arm64Mnemonic.SCVTF, 0b011 => Arm64Mnemonic.UCVTF, + 0b000100 => Arm64Mnemonic.FCVT, _ => throw new("Impossible opcode") }; @@ -205,7 +206,7 @@ public static Arm64Instruction DataProcessingOneSource(uint instruction) 0b000001 => Arm64Mnemonic.FABS, 0b000010 => Arm64Mnemonic.FNEG, 0b000011 => Arm64Mnemonic.FSQRT, - 0b000100 => throw new Arm64UndefinedInstructionException("Floating-point data-processing (1 source): opcode 0b000100 is reserved"), + 0b000100 => Arm64Mnemonic.FCVT, 0b000101 => Arm64Mnemonic.FCVT, 0b000110 => throw new Arm64UndefinedInstructionException("Floating-point data-processing (1 source): opcode 0b000110 is reserved"), 0b000111 => Arm64Mnemonic.FCVT, @@ -240,7 +241,42 @@ public static Arm64Instruction DataProcessingOneSource(uint instruction) var regD = baseReg + rd; var regN = baseReg + rn; - +// Fix FCVT + if (mnemonic == Arm64Mnemonic.FCVT) + { + if (opcode == 0b000100) // FCVT additional variant + { + regD = ptype switch + { + 0b00 => Arm64Register.H0 + rd, // S => H + 0b01 => Arm64Register.S0 + rd, // D => S + 0b11 => Arm64Register.S0 + rd, // H => S + _ => throw new("Impossible ptype") + }; + } + else if (opcode == 0b000101) // FCVT D/S + { + + regD = ptype switch + { + 0b00 => Arm64Register.D0 + rd, // S => D + 0b01 => Arm64Register.S0 + rd, // D => S + 0b11 => Arm64Register.S0 + rd, // H => S + _ => throw new("Impossible ptype") + }; + } + else if (opcode == 0b000111) // FCVT H/D + { + + regD = ptype switch + { + 0b00 => Arm64Register.H0 + rd, // S => H + 0b01 => Arm64Register.H0 + rd, // D => H + 0b11 => Arm64Register.D0 + rd, // H => D + _ => throw new("Impossible ptype") + }; + } + } return new() { Mnemonic = mnemonic, diff --git a/Disarm/InternalDisassembly/Arm64LoadsStores.cs b/Disarm/InternalDisassembly/Arm64LoadsStores.cs index 80a2e43..7773f1a 100644 --- a/Disarm/InternalDisassembly/Arm64LoadsStores.cs +++ b/Disarm/InternalDisassembly/Arm64LoadsStores.cs @@ -449,6 +449,14 @@ private static Arm64Instruction LoadStoreRegisterFromImm(uint instruction, Arm64 3 => Arm64Register.D0, _ => throw new("Impossible size") }, + Arm64Mnemonic.STR or Arm64Mnemonic.LDR when isVector && opc is 1 => size switch + { + 0 => Arm64Register.B0, + 1 => Arm64Register.H0, + 2 => Arm64Register.S0, + 3 => Arm64Register.D0, + _ => throw new("Impossible size") + }, Arm64Mnemonic.STR or Arm64Mnemonic.LDR when isVector => Arm64Register.V0, //128-bit vector Arm64Mnemonic.STRB or Arm64Mnemonic.LDRB or Arm64Mnemonic.STRH or Arm64Mnemonic.LDRH => Arm64Register.W0, Arm64Mnemonic.STR or Arm64Mnemonic.LDR when size is 0b10 => Arm64Register.W0, @@ -702,6 +710,7 @@ private static Arm64Instruction LoadStoreRegFromImmUnsigned(uint instruction) mnemonic = opc == 0b10 ? Arm64Mnemonic.STR : Arm64Mnemonic.LDR; baseReg = Arm64Register.V0; //128-bit variant + immediate <<= 4; } else { @@ -872,6 +881,14 @@ private static Arm64Instruction LoadStoreRegisterFromRegisterOffset(uint instruc 3 => Arm64Register.D0, _ => throw new("Impossible size") }, + Arm64Mnemonic.STR or Arm64Mnemonic.LDR when isVector && opc is 1 => size switch + { + 0 => Arm64Register.B0, + 1 => Arm64Register.H0, + 2 => Arm64Register.S0, + 3 => Arm64Register.D0, + _ => throw new("Impossible size") + }, Arm64Mnemonic.STR or Arm64Mnemonic.LDR when isVector => Arm64Register.V0, //128-bit vector Arm64Mnemonic.STRB or Arm64Mnemonic.LDRB or Arm64Mnemonic.STRH or Arm64Mnemonic.LDRH => Arm64Register.W0, Arm64Mnemonic.STR or Arm64Mnemonic.LDR when size is 0b10 => Arm64Register.W0, @@ -993,6 +1010,14 @@ private static Arm64Instruction LoadStoreRegisterFromImmUnscaled(uint instructio 3 => Arm64Register.D0, _ => throw new("Impossible size") }, + Arm64Mnemonic.STUR or Arm64Mnemonic.LDUR when isVector && opc is 1 => size switch + { + 0 => Arm64Register.B0, + 1 => Arm64Register.H0, + 2 => Arm64Register.S0, + 3 => Arm64Register.D0, + _ => throw new("Impossible size") + }, Arm64Mnemonic.STUR or Arm64Mnemonic.LDUR when isVector => Arm64Register.V0, //128-bit vector Arm64Mnemonic.STURB or Arm64Mnemonic.LDURB or Arm64Mnemonic.STURH or Arm64Mnemonic.LDURH => Arm64Register.W0, Arm64Mnemonic.STUR or Arm64Mnemonic.LDUR when size is 0b10 => Arm64Register.W0, diff --git a/Disarm/InternalDisassembly/Arm64NonScalarAdvancedSimd.cs b/Disarm/InternalDisassembly/Arm64NonScalarAdvancedSimd.cs index 6e1d7bb..23be1d6 100644 --- a/Disarm/InternalDisassembly/Arm64NonScalarAdvancedSimd.cs +++ b/Disarm/InternalDisassembly/Arm64NonScalarAdvancedSimd.cs @@ -14,7 +14,7 @@ public static Arm64Instruction Disassemble(uint instruction) var op3Lo = (op3 & 1) == 1; if (op1 == 0b11) - throw new Arm64UndefinedInstructionException("Advanced SIMD instruction with op1 == 11"); + return AdvancedSimdVectorXIndexedElement(instruction); //Handle the couple of cases where op1 is not simply 0b0x if (op1 == 0b10) @@ -51,7 +51,7 @@ public static Arm64Instruction Disassemble(uint instruction) if (op2 == 0b1111 && (op3 & 0b1_1000_0011) == 0b10) return AdvancedSimdTwoRegisterMiscFp16(instruction); - if ((op2UpperHalf & 1) == 0 && (op3 * 0b100001) == 0b100001) + if ((op2UpperHalf & 1) == 0 && (op3 & 0b100001) == 0b100001) return AdvancedSimdThreeRegExtension(instruction); if (op2 is 0b0100 or 0b1100 && (op3 & 0b110000011) == 0b10) @@ -129,7 +129,9 @@ private static Arm64Instruction AdvancedSimdModifiedImmediate(uint instruction) ? Arm64ArrangementSpecifier.FourS : Arm64ArrangementSpecifier.TwoS; //Single precision - var convertedImmediate = Arm64CommonUtils.AdvancedSimdExpandImmediate(op, (byte)cmode, (byte) immediate); + // Use DecodeFPImm for floating-point immediate values instead of AdvancedSimdExpandImmediate + uint pType = op ? 0b01U : o2 ? 0b10U : 0b00U; // 01=double, 10=half, 00=single + double fpValue = Arm64CommonUtils.DecodeFPImm(pType, (uint)immediate); return new() { @@ -137,8 +139,8 @@ private static Arm64Instruction AdvancedSimdModifiedImmediate(uint instruction) Op0Kind = Arm64OperandKind.Register, Op0Reg = Arm64Register.V0 + rd, Op0Arrangement = arrangement, - Op1Kind = Arm64OperandKind.Immediate, - Op1Imm = (long)convertedImmediate, + Op1Kind = Arm64OperandKind.FloatingPointImmediate, + Op1FpImm = fpValue, MnemonicCategory = Arm64MnemonicCategory.SimdConstantToRegister, }; } @@ -216,9 +218,11 @@ private static Arm64Instruction AdvancedSimdModifiedImmediate(uint instruction) Op0Arrangement = arrangement, Op1Kind = Arm64OperandKind.Immediate, Op1Imm = immediate, - Op2Kind = shiftAmount > 0 ? Arm64OperandKind.Immediate : Arm64OperandKind.None, - Op2Imm = shiftAmount, - Op2ShiftType = shiftAmount > 0 ? Arm64ShiftType.LSL : Arm64ShiftType.NONE, + Op1ShiftType = shiftAmount > 0 ? Arm64ShiftType.LSL : Arm64ShiftType.NONE, + Op2Kind = Arm64OperandKind.None, + Op2Imm = 0, + Op2ShiftType = Arm64ShiftType.NONE, + MemExtendOrShiftAmount = shiftAmount, MnemonicCategory = Arm64MnemonicCategory.SimdConstantToRegister, }; } @@ -234,11 +238,158 @@ private static Arm64Instruction AdvancedSimdShiftByImmediate(uint instruction) private static Arm64Instruction AdvancedSimdVectorXIndexedElement(uint instruction) { - return new() + var q = instruction.TestBit(30); // Bit 30 + var u = instruction.TestBit(29); // Bit 29 + var size = (instruction >> 22) & 0b11; // Bits 22-23 + var l = instruction.TestBit(21); // Bit 21 + var m = instruction.TestBit(20); // Bit 20 + var rm = (int)(instruction >> 16) & 0b1111; // Bits 16-19 + var opcode = (instruction >> 12) & 0b1111; // Bits 12-15 + var h = instruction.TestBit(11); // Bit 11 + var rn = (int)(instruction >> 5) & 0b1_1111; // Bits 5-9 + var rd = (int)instruction & 0b1_1111; // Bits 0-4 + + // Determine mnemonic based on U and opcode + Arm64Mnemonic mnemonic; + if (u) { - Mnemonic = Arm64Mnemonic.UNIMPLEMENTED, + mnemonic = opcode switch + { + 0b1001 when size != 0b01 => Arm64Mnemonic.FMULX, + 0b0010 => Arm64Mnemonic.UMLAL, + 0b0110 => Arm64Mnemonic.UMLSL, + 0b1010 => Arm64Mnemonic.UMULL, + 0b1101 => Arm64Mnemonic.SQRDMLAH, + 0b1111 => Arm64Mnemonic.SQRDMLSH, + _ => throw new Arm64UndefinedInstructionException("AdvancedSimdVectorXIndexedElement: Unallocated U=1 opcode") + }; + } + else + { + mnemonic = opcode switch + { + 0b0001 when size != 0b01 => Arm64Mnemonic.FMLA, + 0b0101 when size != 0b01 => Arm64Mnemonic.FMLS, + 0b1001 when size != 0b01 => Arm64Mnemonic.FMUL, + 0b0010 => Arm64Mnemonic.SMLAL, + 0b0110 => Arm64Mnemonic.SMLSL, + 0b1010 => Arm64Mnemonic.SMULL, + 0b0011 => Arm64Mnemonic.SQDMLAL, + 0b0111 => Arm64Mnemonic.SQDMLSL, + 0b1011 => Arm64Mnemonic.SQDMULL, + 0b1100 => Arm64Mnemonic.SQDMULH, + 0b1101 => Arm64Mnemonic.SQRDMULH, + _ => throw new Arm64UndefinedInstructionException("AdvancedSimdVectorXIndexedElement: Unallocated U=0 opcode") + }; + } + + var result = new Arm64Instruction() + { + Mnemonic = mnemonic, MnemonicCategory = Arm64MnemonicCategory.SimdVectorMath, + Op0Kind = Arm64OperandKind.Register, + Op1Kind = Arm64OperandKind.Register, + Op2Kind = Arm64OperandKind.VectorRegisterElement, }; + + // Set up operands based on instruction type + if (mnemonic is Arm64Mnemonic.FMLA or Arm64Mnemonic.FMLS or Arm64Mnemonic.FMUL or Arm64Mnemonic.FMULX) + { + // Floating-point instructions + var arrangement = size switch + { + 0b00 => q ? Arm64ArrangementSpecifier.EightH : Arm64ArrangementSpecifier.FourH, // FP16 + 0b10 => q ? Arm64ArrangementSpecifier.FourS : Arm64ArrangementSpecifier.TwoS, // Single + 0b11 => q ? Arm64ArrangementSpecifier.TwoD : throw new Arm64UndefinedInstructionException("Reserved"), // Double + _ => throw new Arm64UndefinedInstructionException("Reserved size for FP") + }; + + var elementWidth = size switch + { + 0b00 => Arm64VectorElementWidth.H, + 0b10 => Arm64VectorElementWidth.S, + 0b11 => Arm64VectorElementWidth.D, + _ => throw new Arm64UndefinedInstructionException("Reserved") + }; + + var elementIndex = size switch + { + 0b00 => (h ? 4 : 0) | (l ? 2 : 0) | (m ? 1 : 0), // H:L:M for FP16 + 0b10 => (h ? 2 : 0) | (l ? 1 : 0), // H:L for Single + 0b11 => h ? 1 : 0, // H for Double + _ => throw new Arm64UndefinedInstructionException("Reserved") + }; + + result = result with + { + Op0Reg = Arm64Register.V0 + rd, + Op0Arrangement = arrangement, + Op1Reg = Arm64Register.V0 + rn, + Op1Arrangement = arrangement, + Op2Reg = Arm64Register.V0 + (rm | (m ? 16 : 0)), + Op2VectorElement = new Arm64VectorElement(elementWidth, elementIndex) + }; + } + else + { + // Integer instructions - these have different source and destination arrangements + var srcArrangement = size switch + { + 0b01 => q ? Arm64ArrangementSpecifier.EightH : Arm64ArrangementSpecifier.FourH, + 0b10 => q ? Arm64ArrangementSpecifier.FourS : Arm64ArrangementSpecifier.TwoS, + _ => throw new Arm64UndefinedInstructionException("Reserved size for integer") + }; + + var dstArrangement = size switch + { + 0b01 => q ? Arm64ArrangementSpecifier.FourS : Arm64ArrangementSpecifier.TwoS, + 0b10 => q ? Arm64ArrangementSpecifier.TwoD : Arm64ArrangementSpecifier.None, + _ => throw new Arm64UndefinedInstructionException("Reserved size for integer") + }; + + var elementWidth = size switch + { + 0b01 => Arm64VectorElementWidth.H, + 0b10 => Arm64VectorElementWidth.S, + _ => throw new Arm64UndefinedInstructionException("Reserved") + }; + + var elementIndex = size switch + { + 0b01 => (h ? 4 : 0) | (l ? 2 : 0) | (m ? 1 : 0), // H:L:M for 16-bit + 0b10 => (h ? 2 : 0) | (l ? 1 : 0), // H:L for 32-bit + _ => throw new Arm64UndefinedInstructionException("Reserved") + }; + + // For SQDMULH and SQRDMULH, source and destination have same arrangement + if (mnemonic is Arm64Mnemonic.SQDMULH or Arm64Mnemonic.SQRDMULH or Arm64Mnemonic.SQRDMLAH or Arm64Mnemonic.SQRDMLSH) + { + result = result with + { + Op0Reg = Arm64Register.V0 + rd, + Op0Arrangement = srcArrangement, + Op1Reg = Arm64Register.V0 + rn, + Op1Arrangement = srcArrangement, + Op2Reg = Arm64Register.V0 + (rm | (m ? 16 : 0)), + Op2VectorElement = new Arm64VectorElement(elementWidth, elementIndex) + }; + } + else + { + // For other integer instructions, destination is wider + result = result with + { + Op0Reg = Arm64Register.V0 + rd, + Op0Arrangement = dstArrangement, + Op1Reg = Arm64Register.V0 + rn, + Op1Arrangement = srcArrangement, + Op2Reg = Arm64Register.V0 + (rm | (m ? 16 : 0)), + Op2VectorElement = new Arm64VectorElement(elementWidth, elementIndex) + }; + } + } + + return result; } private static Arm64Instruction AdvancedSimdCopy(uint instruction) @@ -295,6 +446,214 @@ private static Arm64Instruction AdvancedSimdCopy(uint instruction) _ => throw new Arm64UndefinedInstructionException($"AdvancedSimdCopy: imm4 0x{imm4:X} is reserved") }; + if (mnemonic == Arm64Mnemonic.DUP && imm4 == 0b0000) + { + // DUP (element) - duplicate vector element to vector or scalar + Arm64VectorElementWidth elementWidth; + uint elementIndex; + Arm64ArrangementSpecifier arrangement; + + if (imm5.TestBit(0)) + { + // 8-bit elements + elementWidth = Arm64VectorElementWidth.B; + elementIndex = imm5 >> 1; + arrangement = qFlag ? Arm64ArrangementSpecifier.SixteenB : Arm64ArrangementSpecifier.EightB; + } + else if (imm5.TestBit(1)) + { + // 16-bit elements + elementWidth = Arm64VectorElementWidth.H; + elementIndex = imm5 >> 2; + arrangement = qFlag ? Arm64ArrangementSpecifier.EightH : Arm64ArrangementSpecifier.FourH; + } + else if (imm5.TestBit(2)) + { + // 32-bit elements + elementWidth = Arm64VectorElementWidth.S; + elementIndex = imm5 >> 3; + arrangement = qFlag ? Arm64ArrangementSpecifier.FourS : Arm64ArrangementSpecifier.TwoS; + } + else if (imm5.TestBit(3)) + { + // 64-bit elements + elementWidth = Arm64VectorElementWidth.D; + elementIndex = imm5 >> 4; + arrangement = qFlag ? Arm64ArrangementSpecifier.TwoD : throw new Arm64UndefinedInstructionException("DUP: 64-bit scalar not supported in this context"); + } + else + { + throw new Arm64UndefinedInstructionException("DUP: invalid imm5 value"); + } + + return new() + { + Mnemonic = Arm64Mnemonic.DUP, + Op0Kind = Arm64OperandKind.Register, + Op0Reg = Arm64Register.V0 + rd, + Op0Arrangement = arrangement, + Op1Kind = Arm64OperandKind.VectorRegisterElement, + Op1Reg = Arm64Register.V0 + rn, + Op1VectorElement = new Arm64VectorElement(elementWidth, (int)elementIndex), + MnemonicCategory = Arm64MnemonicCategory.SimdRegisterToRegister, + }; + } + + if (mnemonic == Arm64Mnemonic.DUP && imm4 == 0b0001) + { + // DUP (general) - duplicate general register to vector + Arm64ArrangementSpecifier arrangement; + Arm64Register srcReg; + + if (imm5.TestBit(0)) + { + // 8-bit elements + arrangement = qFlag ? Arm64ArrangementSpecifier.SixteenB : Arm64ArrangementSpecifier.EightB; + srcReg = Arm64Register.W0 + rn; + } + else if (imm5.TestBit(1)) + { + // 16-bit elements + arrangement = qFlag ? Arm64ArrangementSpecifier.EightH : Arm64ArrangementSpecifier.FourH; + srcReg = Arm64Register.W0 + rn; + } + else if (imm5.TestBit(2)) + { + // 32-bit elements + arrangement = qFlag ? Arm64ArrangementSpecifier.FourS : Arm64ArrangementSpecifier.TwoS; + srcReg = Arm64Register.W0 + rn; + } + else if (imm5.TestBit(3)) + { + // 64-bit elements + arrangement = qFlag ? Arm64ArrangementSpecifier.TwoD : throw new Arm64UndefinedInstructionException("DUP: 64-bit scalar not supported in this context"); + srcReg = Arm64Register.X0 + rn; + } + else + { + throw new Arm64UndefinedInstructionException("DUP: invalid imm5 value"); + } + + return new() + { + Mnemonic = Arm64Mnemonic.DUP, + Op0Kind = Arm64OperandKind.Register, + Op0Reg = Arm64Register.V0 + rd, + Op0Arrangement = arrangement, + Op1Kind = Arm64OperandKind.Register, + Op1Reg = srcReg, + MnemonicCategory = Arm64MnemonicCategory.SimdRegisterToRegister, + }; + } + + if (mnemonic == Arm64Mnemonic.SMOV || mnemonic == Arm64Mnemonic.UMOV) + { + // SMOV/UMOV - move vector element to general register (with sign/zero extension) + Arm64VectorElementWidth elementWidth; + uint elementIndex; + Arm64Register dstReg; + + if (imm5.TestBit(0)) + { + // 8-bit elements + elementWidth = Arm64VectorElementWidth.B; + elementIndex = imm5 >> 1; + dstReg = qFlag ? Arm64Register.X0 + rd : Arm64Register.W0 + rd; + } + else if (imm5.TestBit(1)) + { + // 16-bit elements + elementWidth = Arm64VectorElementWidth.H; + elementIndex = imm5 >> 2; + dstReg = qFlag ? Arm64Register.X0 + rd : Arm64Register.W0 + rd; + } + else if (imm5.TestBit(2)) + { + // 32-bit elements (only valid for UMOV) + if (mnemonic == Arm64Mnemonic.SMOV) + throw new Arm64UndefinedInstructionException("SMOV: 32-bit elements not supported"); + elementWidth = Arm64VectorElementWidth.S; + elementIndex = imm5 >> 3; + dstReg = qFlag ? Arm64Register.X0 + rd : Arm64Register.W0 + rd; + } + else if (imm5.TestBit(3)) + { + // 64-bit elements (only valid for UMOV with qFlag set) + if (mnemonic == Arm64Mnemonic.SMOV || !qFlag) + throw new Arm64UndefinedInstructionException("UMOV: 64-bit elements only valid with qFlag set"); + elementWidth = Arm64VectorElementWidth.D; + elementIndex = imm5 >> 4; + dstReg = Arm64Register.X0 + rd; + } + else + { + throw new Arm64UndefinedInstructionException("SMOV/UMOV: invalid imm5 value"); + } + + return new() + { + Mnemonic = mnemonic, + Op0Kind = Arm64OperandKind.Register, + Op0Reg = dstReg, + Op1Kind = Arm64OperandKind.VectorRegisterElement, + Op1Reg = Arm64Register.V0 + rn, + Op1VectorElement = new Arm64VectorElement(elementWidth, (int)elementIndex), + MnemonicCategory = Arm64MnemonicCategory.SimdRegisterToRegister, + }; + } + + if (mnemonic == Arm64Mnemonic.INS && imm4 == 0b0011) + { + // INS (general) - insert general register into vector element + Arm64VectorElementWidth elementWidth; + uint elementIndex; + Arm64Register srcReg; + + if (imm5.TestBit(0)) + { + // 8-bit elements + elementWidth = Arm64VectorElementWidth.B; + elementIndex = imm5 >> 1; + srcReg = Arm64Register.W0 + rn; + } + else if (imm5.TestBit(1)) + { + // 16-bit elements + elementWidth = Arm64VectorElementWidth.H; + elementIndex = imm5 >> 2; + srcReg = Arm64Register.W0 + rn; + } + else if (imm5.TestBit(2)) + { + // 32-bit elements + elementWidth = Arm64VectorElementWidth.S; + elementIndex = imm5 >> 3; + srcReg = Arm64Register.W0 + rn; + } + else if (imm5.TestBit(3)) + { + // 64-bit elements + elementWidth = Arm64VectorElementWidth.D; + elementIndex = imm5 >> 4; + srcReg = Arm64Register.X0 + rn; + } + else + { + throw new Arm64UndefinedInstructionException("INS: invalid imm5 value"); + } + + return new() + { + Mnemonic = Arm64Mnemonic.INS, + Op0Kind = Arm64OperandKind.VectorRegisterElement, + Op0Reg = Arm64Register.V0 + rd, + Op0VectorElement = new Arm64VectorElement(elementWidth, (int)elementIndex), + Op1Kind = Arm64OperandKind.Register, + Op1Reg = srcReg, + MnemonicCategory = Arm64MnemonicCategory.SimdRegisterToRegister, + }; + } + return new() { Mnemonic = Arm64Mnemonic.UNIMPLEMENTED, @@ -313,19 +672,87 @@ private static Arm64Instruction AdvancedSimdTableLookup(uint instruction) private static Arm64Instruction AdvancedSimdPermute(uint instruction) { + var q = instruction.TestBit(30); + var size = (instruction >> 22) & 0b11; + var rm = (int)(instruction >> 16) & 0b11111; + var opcode = (instruction >> 12) & 0b111; + var rn = (int)(instruction >> 5) & 0b11111; + var rd = (int)instruction & 0b11111; + + // Determine mnemonic based on opcode + Arm64Mnemonic mnemonic = opcode switch + { + 0b001 => Arm64Mnemonic.UZP1, + 0b101 => Arm64Mnemonic.TRN1, + 0b011 => Arm64Mnemonic.ZIP1, + 0b000 => Arm64Mnemonic.UZP2, + 0b100 => Arm64Mnemonic.TRN2, + 0b010 => Arm64Mnemonic.ZIP2, + _ => throw new Arm64UndefinedInstructionException($"AdvancedSimdPermute: Invalid opcode 0x{opcode:X}") + }; + + // Determine arrangement based on size and q + Arm64ArrangementSpecifier arrangement = size switch + { + 0b00 when q => Arm64ArrangementSpecifier.SixteenB, + 0b00 => Arm64ArrangementSpecifier.EightB, + 0b01 when q => Arm64ArrangementSpecifier.EightH, + 0b01 => Arm64ArrangementSpecifier.FourH, + 0b10 when q => Arm64ArrangementSpecifier.FourS, + 0b10 => Arm64ArrangementSpecifier.TwoS, + 0b11 when q => Arm64ArrangementSpecifier.TwoD, + 0b11 => throw new Arm64UndefinedInstructionException("AdvancedSimdPermute: size=11, q=0 is reserved"), + _ => throw new("Impossible size") + }; + return new() { - Mnemonic = Arm64Mnemonic.UNIMPLEMENTED, - MnemonicCategory = Arm64MnemonicCategory.SimdRegisterToRegister, + Mnemonic = mnemonic, + Op0Kind = Arm64OperandKind.Register, + Op0Reg = Arm64Register.V0 + rd, + Op0Arrangement = arrangement, + Op1Kind = Arm64OperandKind.Register, + Op1Reg = Arm64Register.V0 + rn, + Op1Arrangement = arrangement, + Op2Kind = Arm64OperandKind.Register, + Op2Reg = Arm64Register.V0 + rm, + Op2Arrangement = arrangement, + MnemonicCategory = Arm64MnemonicCategory.SimdVectorMath, }; } private static Arm64Instruction AdvancedSimdExtract(uint instruction) { + var q = instruction.TestBit(30); // Bit 30 - 0 for 64-bit, 1 for 128-bit + var rm = (int)(instruction >> 16) & 0b1111; // Bits 16-19 + var imm4 = (instruction >> 11) & 0b1111; // Bits 11-14 - extraction index + var rn = (int)(instruction >> 5) & 0b11111; // Bits 5-9 + var rd = (int)instruction & 0b11111; // Bits 0-4 + + // EXT only operates on byte elements + var arrangement = q ? Arm64ArrangementSpecifier.SixteenB : Arm64ArrangementSpecifier.EightB; + + // For 64-bit operation (q=0), imm4 must be in range 0-7 + // For 128-bit operation (q=1), imm4 must be in range 0-15 + var maxIndex = q ? 15 : 7; + if (imm4 > maxIndex) + throw new Arm64UndefinedInstructionException($"AdvancedSimdExtract: imm4 {imm4} exceeds maximum {maxIndex} for {(q ? "128" : "64")}-bit operation"); + return new() { - Mnemonic = Arm64Mnemonic.UNIMPLEMENTED, - MnemonicCategory = Arm64MnemonicCategory.SimdRegisterToRegister, + Mnemonic = Arm64Mnemonic.EXT, + Op0Kind = Arm64OperandKind.Register, + Op0Reg = Arm64Register.V0 + rd, + Op0Arrangement = arrangement, + Op1Kind = Arm64OperandKind.Register, + Op1Reg = Arm64Register.V0 + rn, + Op1Arrangement = arrangement, + Op2Kind = Arm64OperandKind.Register, + Op2Reg = Arm64Register.V0 + rm, + Op2Arrangement = arrangement, + Op3Kind = Arm64OperandKind.Immediate, + Op3Imm = imm4, + MnemonicCategory = Arm64MnemonicCategory.SimdVectorMath, }; } @@ -358,10 +785,107 @@ private static Arm64Instruction AdvancedSimdThreeRegExtension(uint instruction) private static Arm64Instruction AdvancedSimdTwoRegisterMisc(uint instruction) { + var q = instruction.TestBit(30); + var u = instruction.TestBit(29); + var size = (instruction >> 22) & 0b11; + var opcode = (instruction >> 12) & 0b1_1111; + var rn = (int)((instruction >> 5) & 0b1_1111); + var rd = (int)(instruction & 0b1_1111); + + Arm64Mnemonic mnemonic; + + if (u) + { + mnemonic = opcode switch + { + 0b00000 when size != 0b11 => Arm64Mnemonic.REV32, + 0b00001 when size is 0b00 or 0b01 => Arm64Mnemonic.REV16, + 0b00010 => Arm64Mnemonic.UADDLP, + 0b00011 => Arm64Mnemonic.USQADD, + 0b00100 => Arm64Mnemonic.CLZ, + 0b00101 => Arm64Mnemonic.CLZ, + 0b00111 => Arm64Mnemonic.SQNEG, + 0b01000 => Arm64Mnemonic.CMGE, + 0b01001 => Arm64Mnemonic.CMLE, + 0b01010 => Arm64Mnemonic.FCMGT, + 0b01011 => Arm64Mnemonic.FCMGE, + 0b10010 => Arm64Mnemonic.SQXTUN, + 0b10100 => Arm64Mnemonic.UQXTN, + 0b10110 when size != 0b11 => Arm64Mnemonic.FCVTXN, + 0b11000 when size != 0b11 => Arm64Mnemonic.FRINTA, + 0b11001 when size != 0b11 => Arm64Mnemonic.FRINTX, + 0b11010 when size != 0b11 => Arm64Mnemonic.FCVTNU, + 0b11011 when size != 0b11 => Arm64Mnemonic.FCVTMU, + 0b11100 when size != 0b11 => Arm64Mnemonic.FCVTAU, + 0b11101 when size != 0b11 => Arm64Mnemonic.UCVTF, + 0b11111 when size != 0b11 => Arm64Mnemonic.FRSQRTE, + _ => throw new Arm64UndefinedInstructionException($"AdvancedSimdTwoRegisterMisc: U=1, opcode=0x{opcode:X}, size=0x{size:X}") + }; + } + else + { + mnemonic = opcode switch + { + 0b00000 when size != 0b11 => Arm64Mnemonic.REV64, + 0b00001 when size is 0b00 or 0b01 => Arm64Mnemonic.REV32, + 0b00010 => Arm64Mnemonic.SADDLP, + 0b00011 => Arm64Mnemonic.SUQADD, + 0b00100 => Arm64Mnemonic.CLS, + 0b00101 when size is 0b00 => Arm64Mnemonic.CNT, + 0b00101 => throw new Arm64UndefinedInstructionException($"AdvancedSimdTwoRegisterMisc: U=0, opcode=0b00101, size=0x{size:X} (CNT only valid for size=0b00)"), + 0b00110 => Arm64Mnemonic.SADALP, + 0b00111 => Arm64Mnemonic.SQABS, + 0b01000 => Arm64Mnemonic.CMGT, + 0b01001 => Arm64Mnemonic.CMEQ, + 0b01010 => Arm64Mnemonic.CMLT, + 0b01011 => Arm64Mnemonic.ABS, + 0b10010 => Arm64Mnemonic.XTN, + 0b10100 => Arm64Mnemonic.SQXTN, + 0b10110 when size != 0b11 => Arm64Mnemonic.FCVTN, + 0b10111 when size != 0b11 => Arm64Mnemonic.FCVTL, + 0b11000 when size != 0b11 => Arm64Mnemonic.FRINTN, + 0b11001 when size != 0b11 => Arm64Mnemonic.FRINTM, + 0b11010 when size != 0b11 => Arm64Mnemonic.FCVTNS, + 0b11011 when size != 0b11 => Arm64Mnemonic.FCVTMS, + 0b11100 when size != 0b11 => Arm64Mnemonic.FCVTAS, + 0b11101 when size != 0b11 => Arm64Mnemonic.SCVTF, + 0b11111 when size != 0b11 => Arm64Mnemonic.FRECPE, + _ => throw new Arm64UndefinedInstructionException($"AdvancedSimdTwoRegisterMisc: U=0, opcode=0x{opcode:X}, size=0x{size:X}") + }; + } + + // Determine arrangement based on size and q + Arm64ArrangementSpecifier arrangement = size switch + { + 0b00 when q => Arm64ArrangementSpecifier.SixteenB, + 0b00 => Arm64ArrangementSpecifier.EightB, + 0b01 when q => Arm64ArrangementSpecifier.EightH, + 0b01 => Arm64ArrangementSpecifier.FourH, + 0b10 when q => Arm64ArrangementSpecifier.FourS, + 0b10 => Arm64ArrangementSpecifier.TwoS, + 0b11 when q => Arm64ArrangementSpecifier.TwoD, + 0b11 => Arm64ArrangementSpecifier.None, // Scalar D register + _ => throw new("Impossible size") + }; + + var category = mnemonic switch + { + Arm64Mnemonic.CMGT or Arm64Mnemonic.CMEQ or Arm64Mnemonic.CMLT or + Arm64Mnemonic.CMGE or Arm64Mnemonic.CMLE or + Arm64Mnemonic.FCMEQ or Arm64Mnemonic.FCMGE or Arm64Mnemonic.FCMGT => Arm64MnemonicCategory.SimdComparison, + _ => Arm64MnemonicCategory.SimdVectorMath, + }; + return new() { - Mnemonic = Arm64Mnemonic.UNIMPLEMENTED, - MnemonicCategory = Arm64MnemonicCategory.Unspecified, //could be comparison, math, general data processing, or comparison + Mnemonic = mnemonic, + Op0Kind = Arm64OperandKind.Register, + Op0Reg = Arm64Register.V0 + rd, + Op0Arrangement = arrangement, + Op1Kind = Arm64OperandKind.Register, + Op1Reg = Arm64Register.V0 + rn, + Op1Arrangement = arrangement, + MnemonicCategory = category, }; } @@ -511,7 +1035,51 @@ private static Arm64Instruction AdvancedSimdThreeSame(uint instruction) if (u) mnemonic = opcode switch { - _ => throw new NotImplementedException() + 0b00000 => Arm64Mnemonic.UHADD, + 0b00001 => Arm64Mnemonic.UQADD, + 0b00010 => Arm64Mnemonic.URHADD, + 0b00011 when size is 0b00 => Arm64Mnemonic.EOR, + 0b00011 when size is 0b01 => Arm64Mnemonic.BSL, + 0b00011 when size is 0b10 => Arm64Mnemonic.BIT, + 0b00011 when size is 0b11 => Arm64Mnemonic.BIF, + 0b00100 => Arm64Mnemonic.UHSUB, + 0b00101 => Arm64Mnemonic.UQSUB, + 0b00110 => Arm64Mnemonic.CMHI, + 0b00111 => Arm64Mnemonic.CMHS, + 0b01000 => Arm64Mnemonic.USHL, + 0b01001 => Arm64Mnemonic.UQSHL, + 0b01010 => Arm64Mnemonic.URSHL, + 0b01011 => Arm64Mnemonic.UQRSHL, + 0b01100 => Arm64Mnemonic.UMAX, + 0b01101 => Arm64Mnemonic.UMIN, + 0b01110 => Arm64Mnemonic.UABD, + 0b01111 => Arm64Mnemonic.UABA, + 0b10000 => Arm64Mnemonic.SUB, + 0b10001 => Arm64Mnemonic.CMEQ, + 0b10010 => Arm64Mnemonic.MLS, + 0b10011 => Arm64Mnemonic.PMUL, + 0b10100 => Arm64Mnemonic.UMAXP, + 0b10101 => Arm64Mnemonic.UMINP, + 0b10110 => Arm64Mnemonic.SQRDMULH, + 0b10111 when size is 0b00 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b10111, size=0b00"), + 0b10111 when size is 0b01 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b10111, size=0b01"), + 0b10111 when size is 0b10 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b10111, size=0b10"), + 0b10111 when size is 0b11 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b10111, size=0b11"), + 0b11000 when !sizeHi => Arm64Mnemonic.FMAXNMP, + 0b11000 => Arm64Mnemonic.FMINNMP, + 0b11001 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b11001"), + 0b11010 when !sizeHi => Arm64Mnemonic.FADDP, + 0b11010 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b11010 with high size bit set"), + 0b11011 when !sizeHi => Arm64Mnemonic.FMUL, + 0b11011 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b11011 with high size bit set"), + 0b11100 when !sizeHi => Arm64Mnemonic.FCMGE, + 0b11100 => Arm64Mnemonic.FCMGT, + 0b11101 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b11101"), + 0b11110 when !sizeHi => Arm64Mnemonic.FMAXP, + 0b11110 => Arm64Mnemonic.FMINP, + 0b11111 when !sizeHi => Arm64Mnemonic.FDIV, + 0b11111 => throw new Arm64UndefinedInstructionException("Advanced SIMD three same: U=1, opcode=0b11111 with high size bit set"), + _ => throw new("Impossible opcode") }; else mnemonic = opcode switch @@ -566,7 +1134,9 @@ private static Arm64Instruction AdvancedSimdThreeSame(uint instruction) var category = mnemonic switch { - Arm64Mnemonic.CMGT or Arm64Mnemonic.CMGE or Arm64Mnemonic.CMTST => Arm64MnemonicCategory.SimdComparison, + Arm64Mnemonic.CMGT or Arm64Mnemonic.CMGE or Arm64Mnemonic.CMTST or + Arm64Mnemonic.CMHI or Arm64Mnemonic.CMHS or Arm64Mnemonic.CMEQ or + Arm64Mnemonic.FCMEQ or Arm64Mnemonic.FCMGE or Arm64Mnemonic.FCMGT => Arm64MnemonicCategory.SimdComparison, _ => Arm64MnemonicCategory.SimdVectorMath, }; @@ -578,7 +1148,8 @@ private static Arm64Instruction AdvancedSimdThreeSame(uint instruction) Arm64ArrangementSpecifier arrangement; Arm64Register baseReg; - if (mnemonic is Arm64Mnemonic.AND or Arm64Mnemonic.BIC or Arm64Mnemonic.ORR or Arm64Mnemonic.ORN) + if (mnemonic is Arm64Mnemonic.AND or Arm64Mnemonic.BIC or Arm64Mnemonic.ORR or Arm64Mnemonic.ORN or + Arm64Mnemonic.EOR or Arm64Mnemonic.BSL or Arm64Mnemonic.BIT or Arm64Mnemonic.BIF) { baseReg = Arm64Register.V0; arrangement = q ? Arm64ArrangementSpecifier.SixteenB : Arm64ArrangementSpecifier.EightB; @@ -586,15 +1157,7 @@ private static Arm64Instruction AdvancedSimdThreeSame(uint instruction) else if (opcode < 0b11000) { //"Simple" instructions - baseReg = size switch - { - //TODO This logic is wrong for some instructions (e.g. SMIN), revisit - 0b00 => Arm64Register.B0, - 0b01 => Arm64Register.H0, - 0b10 => Arm64Register.S0, - 0b11 => Arm64Register.D0, - _ => throw new("Impossible size") - }; + baseReg = Arm64Register.V0; //This logic should be ok though arrangement = size switch @@ -605,6 +1168,8 @@ private static Arm64Instruction AdvancedSimdThreeSame(uint instruction) 0b01 => Arm64ArrangementSpecifier.FourH, 0b10 when q => Arm64ArrangementSpecifier.FourS, 0b10 => Arm64ArrangementSpecifier.TwoS, + 0b11 when q => Arm64ArrangementSpecifier.TwoD, + 0b11 => Arm64ArrangementSpecifier.None, // Scalar D register _ => throw new("Impossible size") }; }