diff --git a/X86Disassembler/X86/Disassembler.cs b/X86Disassembler/X86/Disassembler.cs index a20e01b..0f3e70d 100644 --- a/X86Disassembler/X86/Disassembler.cs +++ b/X86Disassembler/X86/Disassembler.cs @@ -85,13 +85,16 @@ public class Disassembler break; } - // If no special case applies, decode normally + // Store the position before decoding to handle prefixes properly + int startPosition = position; + + // Decode the instruction Instruction? instruction = decoder.DecodeInstruction(); if (instruction != null) { // Adjust the instruction address to include the base address - instruction.Address += _baseAddress; + instruction.Address = _baseAddress + (uint)startPosition; // Add the instruction to the list instructions.Add(instruction); @@ -103,7 +106,7 @@ public class Disassembler Instruction dummyInstruction = new Instruction { - Address = _baseAddress + (uint) position, + Address = _baseAddress + (uint)position, Type = InstructionType.Unknown, StructuredOperands = [OperandFactory.CreateImmediateOperand(unknownByte, 8),] }; diff --git a/X86Disassembler/X86/Handlers/Adc/AdcAccumulatorImmHandler.cs b/X86Disassembler/X86/Handlers/Adc/AdcAccumulatorImmHandler.cs new file mode 100644 index 0000000..48dcc74 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcAccumulatorImmHandler.cs @@ -0,0 +1,71 @@ +namespace X86Disassembler.X86.Handlers.Adc; + +using Operands; + +/// +/// Handler for ADC AX/EAX, imm16/32 instruction (opcode 0x15) +/// +public class AdcAccumulatorImmHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcAccumulatorImmHandler class + /// + /// The instruction decoder that owns this handler + public AdcAccumulatorImmHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // ADC AX/EAX, imm16/32 is encoded as 0x15 + return opcode == 0x15; + } + + /// + /// Decodes a ADC AX/EAX, imm16/32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Determine operand size based on prefix + int operandSize = Decoder.HasOperandSizePrefix() ? 16 : 32; + + // Check if we have enough bytes for the immediate value + if (operandSize == 16 && !Decoder.CanReadUShort()) + { + return false; + } + else if (operandSize == 32 && !Decoder.CanReadUInt()) + { + return false; + } + + // Create the accumulator register operand (AX or EAX) + var accumulatorOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, operandSize); + + // Read and create the immediate operand based on operand size + var immOperand = operandSize == 16 + ? OperandFactory.CreateImmediateOperand(Decoder.ReadUInt16(), operandSize) + : OperandFactory.CreateImmediateOperand(Decoder.ReadUInt32(), operandSize); + + // Set the structured operands + instruction.StructuredOperands = + [ + accumulatorOperand, + immOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcAlImmHandler.cs b/X86Disassembler/X86/Handlers/Adc/AdcAlImmHandler.cs new file mode 100644 index 0000000..dd5ae19 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcAlImmHandler.cs @@ -0,0 +1,64 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Adc; + +/// +/// Handler for ADC AL, imm8 instruction (0x14) +/// +public class AdcAlImmHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcAlImmHandler class + /// + /// The instruction decoder that owns this handler + public AdcAlImmHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + return opcode == 0x14; + } + + /// + /// Decodes an ADC AL, imm8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Check if we have enough bytes for the immediate value + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the immediate byte + var imm8 = Decoder.ReadByte(); + + // Create the AL register operand + var destinationOperand = OperandFactory.CreateRegisterOperand8(RegisterIndex8.AL); + + // Create the immediate operand + var sourceOperand = OperandFactory.CreateImmediateOperand(imm8); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcImmToRm16Handler.cs b/X86Disassembler/X86/Handlers/Adc/AdcImmToRm16Handler.cs new file mode 100644 index 0000000..5696960 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcImmToRm16Handler.cs @@ -0,0 +1,82 @@ +namespace X86Disassembler.X86.Handlers.Adc; + +using Operands; + +/// +/// Handler for ADC r/m16, imm16 instruction (0x81 /2 with 0x66 prefix) +/// +public class AdcImmToRm16Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcImmToRm16Handler class + /// + /// The instruction decoder that owns this handler + public AdcImmToRm16Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // ADC r/m16, imm16 is encoded as 0x81 /2 with 0x66 prefix + if (opcode != 0x81) + { + return false; + } + + // Check if we have enough bytes to read the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Check if the reg field of the ModR/M byte is 2 (ADC) + var reg = ModRMDecoder.PeakModRMReg(); + + // Only handle when the operand size prefix is present + return reg == 2 && Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a ADC r/m16, imm16 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Read the ModR/M byte, specifying that we're dealing with 16-bit operands + var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM16(); + + // Note: The operand size is already set to 16-bit by the ReadModRM16 method + + // Check if we have enough bytes for the immediate value + if (!Decoder.CanReadUShort()) + { + return false; + } + + // Read the immediate value + ushort imm16 = Decoder.ReadUInt16(); + + // Create the immediate operand + var sourceOperand = OperandFactory.CreateImmediateOperand(imm16, 16); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcImmToRm16SignExtendedHandler.cs b/X86Disassembler/X86/Handlers/Adc/AdcImmToRm16SignExtendedHandler.cs new file mode 100644 index 0000000..49b031f --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcImmToRm16SignExtendedHandler.cs @@ -0,0 +1,84 @@ +namespace X86Disassembler.X86.Handlers.Adc; + +using Operands; + +/// +/// Handler for ADC r/m16, imm8 (sign-extended) instruction (0x83 /2 with 0x66 prefix) +/// +public class AdcImmToRm16SignExtendedHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcImmToRm16SignExtendedHandler class + /// + /// The instruction decoder that owns this handler + public AdcImmToRm16SignExtendedHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // ADC r/m16, imm8 (sign-extended) is encoded as 0x83 /2 with 0x66 prefix + if (opcode != 0x83) + { + return false; + } + + // Check if we have enough bytes to read the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Check if the reg field of the ModR/M byte is 2 (ADC) + var reg = ModRMDecoder.PeakModRMReg(); + + // Only handle when the operand size prefix is present + return reg == 2 && Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a ADC r/m16, imm8 (sign-extended) instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // For ADC r/m16, imm8 (sign-extended) (0x83 /2 with 0x66 prefix): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The immediate value is the source operand (sign-extended from 8 to 16 bits) + var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM16(); + + // Note: The operand size is already set to 16-bit by the ReadModRM16 method + + // Check if we have enough bytes for the immediate value + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the immediate value (sign-extended from 8 to 16 bits) + short imm16 = (sbyte)Decoder.ReadByte(); + + // Create the immediate operand + var sourceOperand = OperandFactory.CreateImmediateOperand((ushort)imm16, 16); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcImmToRm8Handler.cs b/X86Disassembler/X86/Handlers/Adc/AdcImmToRm8Handler.cs new file mode 100644 index 0000000..281bf82 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcImmToRm8Handler.cs @@ -0,0 +1,81 @@ +namespace X86Disassembler.X86.Handlers.Adc; + +using Operands; + +/// +/// Handler for ADC r/m8, imm8 instruction (0x80 /2) +/// +public class AdcImmToRm8Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcImmToRm8Handler class + /// + /// The instruction decoder that owns this handler + public AdcImmToRm8Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + if (opcode != 0x80) + return false; + + // Check if the reg field of the ModR/M byte is 2 (ADC) + if (!Decoder.CanReadByte()) + return false; + + var reg = ModRMDecoder.PeakModRMReg(); + + return reg == 2; // 2 = ADC + } + + /// + /// Decodes an ADC r/m8, imm8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For ADC r/m8, imm8 (0x80 /2): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The immediate value is the source operand + var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM8(); + + // Check if we have enough bytes for the immediate value + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the immediate value + byte imm8 = Decoder.ReadByte(); + + // Create the immediate operand + var sourceOperand = OperandFactory.CreateImmediateOperand(imm8, 8); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcR16Rm16Handler.cs b/X86Disassembler/X86/Handlers/Adc/AdcR16Rm16Handler.cs new file mode 100644 index 0000000..fbacedd --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcR16Rm16Handler.cs @@ -0,0 +1,72 @@ +namespace X86Disassembler.X86.Handlers.Adc; + +using Operands; + +/// +/// Handler for ADC r16, r/m16 instruction (0x13 with 0x66 prefix) +/// +public class AdcR16Rm16Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcR16Rm16Handler class + /// + /// The instruction decoder that owns this handler + public AdcR16Rm16Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // ADC r16, r/m16 is encoded as 0x13 with 0x66 prefix + if (opcode != 0x13) + { + return false; + } + + // Only handle when the operand size prefix is present + return Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a ADC r16, r/m16 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // For ADC r16, r/m16 (0x13 with 0x66 prefix): + // - The reg field of the ModR/M byte specifies the destination register + // - The r/m field with mod specifies the source operand (register or memory) + var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM16(); + + // Note: The operand size is already set to 16-bit by the ReadModRM16 method + + // Create the destination register operand with 16-bit size + var destinationOperand = OperandFactory.CreateRegisterOperand(reg, 16); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Adc/AdcR32Rm32Handler.cs new file mode 100644 index 0000000..8759f7f --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcR32Rm32Handler.cs @@ -0,0 +1,66 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Adc; + +/// +/// Handler for ADC r32, r/m32 instruction (0x13) +/// +public class AdcR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcR32Rm32Handler class + /// + /// The instruction decoder that owns this handler + public AdcR32Rm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // Only handle opcode 0x13 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x13 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes an ADC r32, r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For ADC r32, r/m32 (0x13): + // - The reg field specifies the destination register + // - The r/m field with mod specifies the source operand (register or memory) + var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM(); + + // Create the register operand for the reg field + var destinationOperand = OperandFactory.CreateRegisterOperand(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcR8Rm8Handler.cs b/X86Disassembler/X86/Handlers/Adc/AdcR8Rm8Handler.cs new file mode 100644 index 0000000..0005c83 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcR8Rm8Handler.cs @@ -0,0 +1,64 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Adc; + +/// +/// Handler for ADC r8, r/m8 instruction (0x12) +/// +public class AdcR8Rm8Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcR8Rm8Handler class + /// + /// The instruction decoder that owns this handler + public AdcR8Rm8Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + return opcode == 0x12; + } + + /// + /// Decodes an ADC r8, r/m8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For ADC r8, r/m8 (0x12): + // - The reg field specifies the destination register + // - The r/m field with mod specifies the source operand (register or memory) + var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM8(); + + // Create the register operand for the reg field + var destinationOperand = OperandFactory.CreateRegisterOperand8(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcRm16R16Handler.cs b/X86Disassembler/X86/Handlers/Adc/AdcRm16R16Handler.cs new file mode 100644 index 0000000..7a361b9 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcRm16R16Handler.cs @@ -0,0 +1,72 @@ +namespace X86Disassembler.X86.Handlers.Adc; + +using Operands; + +/// +/// Handler for ADC r/m16, r16 instruction (0x11 with 0x66 prefix) +/// +public class AdcRm16R16Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcRm16R16Handler class + /// + /// The instruction decoder that owns this handler + public AdcRm16R16Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // ADC r/m16, r16 is encoded as 0x11 with 0x66 prefix + if (opcode != 0x11) + { + return false; + } + + // Only handle when the operand size prefix is present + return Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a ADC r/m16, r16 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // For ADC r/m16, r16 (0x11 with 0x66 prefix): + // - The reg field of the ModR/M byte specifies the source register + // - The r/m field with mod specifies the destination operand (register or memory) + var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM16(); + + // Note: The operand size is already set to 16-bit by the ReadModRM16 method + + // Create the source register operand with 16-bit size + var sourceOperand = OperandFactory.CreateRegisterOperand(reg, 16); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcRm32R32Handler.cs b/X86Disassembler/X86/Handlers/Adc/AdcRm32R32Handler.cs new file mode 100644 index 0000000..077f57e --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcRm32R32Handler.cs @@ -0,0 +1,66 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Adc; + +/// +/// Handler for ADC r/m32, r32 instruction (0x11) +/// +public class AdcRm32R32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcRm32R32Handler class + /// + /// The instruction decoder that owns this handler + public AdcRm32R32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // Only handle opcode 0x11 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x11 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes an ADC r/m32, r32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For ADC r/m32, r32 (0x11): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The reg field specifies the source register + var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Create the register operand for the reg field + var sourceOperand = OperandFactory.CreateRegisterOperand(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Adc/AdcRm8R8Handler.cs b/X86Disassembler/X86/Handlers/Adc/AdcRm8R8Handler.cs new file mode 100644 index 0000000..e65ebc9 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Adc/AdcRm8R8Handler.cs @@ -0,0 +1,64 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Adc; + +/// +/// Handler for ADC r/m8, r8 instruction (0x10) +/// +public class AdcRm8R8Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AdcRm8R8Handler class + /// + /// The instruction decoder that owns this handler + public AdcRm8R8Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + return opcode == 0x10; + } + + /// + /// Decodes an ADC r/m8, r8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Adc; + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For ADC r/m8, r8 (0x10): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The reg field specifies the source register + var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM8(); + + // Create the register operand for the reg field + var sourceOperand = OperandFactory.CreateRegisterOperand8(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs index 9b90a31..93a2ebe 100644 --- a/X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs @@ -23,7 +23,9 @@ public class AddR32Rm32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x03; + // Only handle opcode 0x03 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x03 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs b/X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs index 4b3274f..dafdbd7 100644 --- a/X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs +++ b/X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs @@ -23,7 +23,9 @@ public class AddRm32R32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x01; + // Only handle opcode 0x01 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x01 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/And/AndMemRegHandler.cs b/X86Disassembler/X86/Handlers/And/AndMemRegHandler.cs index c721469..e950995 100644 --- a/X86Disassembler/X86/Handlers/And/AndMemRegHandler.cs +++ b/X86Disassembler/X86/Handlers/And/AndMemRegHandler.cs @@ -23,7 +23,9 @@ public class AndMemRegHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x21; + // Only handle opcode 0x21 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x21 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/And/AndR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/And/AndR32Rm32Handler.cs index 017fc21..3ce53f7 100644 --- a/X86Disassembler/X86/Handlers/And/AndR32Rm32Handler.cs +++ b/X86Disassembler/X86/Handlers/And/AndR32Rm32Handler.cs @@ -23,7 +23,9 @@ public class AndR32Rm32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x23; + // Only handle opcode 0x23 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x23 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Bit/BsfR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Bit/BsfR32Rm32Handler.cs new file mode 100644 index 0000000..c0d7f7b --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BsfR32Rm32Handler.cs @@ -0,0 +1,84 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BSF r32, r/m32 instruction (0F BC) +/// +public class BsfR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the BsfR32Rm32Handler class + /// + /// The instruction decoder that owns this handler + public BsfR32Rm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BSF r32, r/m32 is a two-byte opcode: 0F BC + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Check if the second byte is BC + var secondByte = Decoder.PeakByte(); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return secondByte == 0xBC && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BSF r32, r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Bsf; + + // Read the second opcode byte (BC) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BSF r32, r/m32 (0F BC): + // - The reg field specifies the destination register + // - The r/m field with mod specifies the source operand (register or memory) + var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM(); + + // Create the register operand for the reg field + var destinationOperand = OperandFactory.CreateRegisterOperand(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BsrR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Bit/BsrR32Rm32Handler.cs new file mode 100644 index 0000000..5e54bd3 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BsrR32Rm32Handler.cs @@ -0,0 +1,84 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BSR r32, r/m32 instruction (0F BD) +/// +public class BsrR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the BsrR32Rm32Handler class + /// + /// The instruction decoder that owns this handler + public BsrR32Rm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BSR r32, r/m32 is a two-byte opcode: 0F BD + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Check if the second byte is BD + var secondByte = Decoder.PeakByte(); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return secondByte == 0xBD && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BSR r32, r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Bsr; + + // Read the second opcode byte (BD) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BSR r32, r/m32 (0F BD): + // - The reg field specifies the destination register + // - The r/m field with mod specifies the source operand (register or memory) + var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM(); + + // Create the register operand for the reg field + var destinationOperand = OperandFactory.CreateRegisterOperand(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + sourceOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BtR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Bit/BtR32Rm32Handler.cs new file mode 100644 index 0000000..3248713 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BtR32Rm32Handler.cs @@ -0,0 +1,84 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BT r32, r/m32 instruction (0F A3) +/// +public class BtR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the BtR32Rm32Handler class + /// + /// The instruction decoder that owns this handler + public BtR32Rm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BT r32, r/m32 is a two-byte opcode: 0F A3 + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Check if the second byte is A3 + var secondByte = Decoder.PeakByte(); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return secondByte == 0xA3 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BT r32, r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Bt; + + // Read the second opcode byte (A3) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BT r/m32, r32 (0F A3): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The reg field specifies the bit index register + var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Create the register operand for the reg field + var bitIndexOperand = OperandFactory.CreateRegisterOperand(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + bitIndexOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BtRm32ImmHandler.cs b/X86Disassembler/X86/Handlers/Bit/BtRm32ImmHandler.cs new file mode 100644 index 0000000..cd9562f --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BtRm32ImmHandler.cs @@ -0,0 +1,101 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BT r/m32, imm8 instruction (0F BA /4) +/// +public class BtRm32ImmHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the BtRm32ImmHandler class + /// + /// The instruction decoder that owns this handler + public BtRm32ImmHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BT r/m32, imm8 is encoded as 0F BA /4 + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanRead(2)) + { + return false; + } + + var (secondByte, modRm) = Decoder.PeakTwoBytes(); + + // Check if the second byte is BA + if (secondByte != 0xBA) + { + return false; + } + + // Check if the reg field of the ModR/M byte is 4 (BT) + var reg = ModRMDecoder.GetRegFromModRM(modRm); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 4 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BT r/m32, imm8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Bt; + + // Read the second opcode byte (BA) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BT r/m32, imm8 (0F BA /4): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The immediate value specifies the bit index + var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Check if we have enough bytes for the immediate value + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the immediate byte for the bit position + byte imm8 = Decoder.ReadByte(); + + // Create the immediate operand + var bitIndexOperand = OperandFactory.CreateImmediateOperand(imm8, 8); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + bitIndexOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BtcR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Bit/BtcR32Rm32Handler.cs new file mode 100644 index 0000000..27a2fd2 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BtcR32Rm32Handler.cs @@ -0,0 +1,84 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BTC r32, r/m32 instruction (0F BB) +/// +public class BtcR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the BtcR32Rm32Handler class + /// + /// The instruction decoder that owns this handler + public BtcR32Rm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BTC r32, r/m32 is a two-byte opcode: 0F BB + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Check if the second byte is BB + var secondByte = Decoder.PeakByte(); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return secondByte == 0xBB && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BTC r32, r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Btc; + + // Read the second opcode byte (BB) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BTC r/m32, r32 (0F BB): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The reg field specifies the bit index register + var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Create the register operand for the reg field + var bitIndexOperand = OperandFactory.CreateRegisterOperand(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + bitIndexOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BtcRm32ImmHandler.cs b/X86Disassembler/X86/Handlers/Bit/BtcRm32ImmHandler.cs new file mode 100644 index 0000000..964f56b --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BtcRm32ImmHandler.cs @@ -0,0 +1,101 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BTC r/m32, imm8 instruction (0F BA /7) +/// +public class BtcRm32ImmHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the BtcRm32ImmHandler class + /// + /// The instruction decoder that owns this handler + public BtcRm32ImmHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BTC r/m32, imm8 is encoded as 0F BA /7 + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanRead(2)) + { + return false; + } + + var (secondByte, modRm) = Decoder.PeakTwoBytes(); + + // Check if the second byte is BA + if (secondByte != 0xBA) + { + return false; + } + + // Check if the reg field of the ModR/M byte is 7 (BTC) + var reg = ModRMDecoder.GetRegFromModRM(modRm); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 7 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BTC r/m32, imm8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Btc; + + // Read the second opcode byte (BA) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BTC r/m32, imm8 (0F BA /7): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The immediate value specifies the bit index + var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Check if we have enough bytes for the immediate value + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the immediate byte for the bit position + byte imm8 = Decoder.ReadByte(); + + // Create the immediate operand + var bitIndexOperand = OperandFactory.CreateImmediateOperand(imm8, 8); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + bitIndexOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BtrR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Bit/BtrR32Rm32Handler.cs new file mode 100644 index 0000000..4775caa --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BtrR32Rm32Handler.cs @@ -0,0 +1,84 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BTR r32, r/m32 instruction (0F B3) +/// +public class BtrR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the BtrR32Rm32Handler class + /// + /// The instruction decoder that owns this handler + public BtrR32Rm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BTR r32, r/m32 is a two-byte opcode: 0F B3 + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Check if the second byte is B3 + var secondByte = Decoder.PeakByte(); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return secondByte == 0xB3 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BTR r32, r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Btr; + + // Read the second opcode byte (B3) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BTR r/m32, r32 (0F B3): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The reg field specifies the bit index register + var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Create the register operand for the reg field + var bitIndexOperand = OperandFactory.CreateRegisterOperand(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + bitIndexOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BtrRm32ImmHandler.cs b/X86Disassembler/X86/Handlers/Bit/BtrRm32ImmHandler.cs new file mode 100644 index 0000000..eeeece1 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BtrRm32ImmHandler.cs @@ -0,0 +1,102 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BTR r/m32, imm8 instruction (0F BA /6) +/// +public class BtrRm32ImmHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the BtrRm32ImmHandler class + /// + /// The instruction decoder that owns this handler + public BtrRm32ImmHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BTR r/m32, imm8 is encoded as 0F BA /6 + if (opcode != 0x0F) + { + return false; + } + + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanRead(2)) + { + return false; + } + + var (secondByte, modRm) = Decoder.PeakTwoBytes(); + + // Check if the second byte is BA + if (secondByte != 0xBA) + { + return false; + } + + // Check if the reg field of the ModR/M byte is 6 (BTR) + var reg = ModRMDecoder.GetRegFromModRM(modRm); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 6 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BTR r/m32, imm8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Btr; + + // Read the second opcode byte (BA) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BTR r/m32, imm8 (0F BA /6): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The immediate value specifies the bit index + var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Check if we have enough bytes for the immediate value + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the immediate byte for the bit position + byte imm8 = Decoder.ReadByte(); + + // Create the immediate operand + var bitIndexOperand = OperandFactory.CreateImmediateOperand(imm8, 8); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + bitIndexOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BtsR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Bit/BtsR32Rm32Handler.cs new file mode 100644 index 0000000..66aad22 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BtsR32Rm32Handler.cs @@ -0,0 +1,84 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BTS r32, r/m32 instruction (0F AB) +/// +public class BtsR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the BtsR32Rm32Handler class + /// + /// The instruction decoder that owns this handler + public BtsR32Rm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BTS r32, r/m32 is a two-byte opcode: 0F AB + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Check if the second byte is AB + var secondByte = Decoder.PeakByte(); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return secondByte == 0xAB && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BTS r32, r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Bts; + + // Read the second opcode byte (AB) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BTS r/m32, r32 (0F AB): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The reg field specifies the bit index register + var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Create the register operand for the reg field + var bitIndexOperand = OperandFactory.CreateRegisterOperand(reg); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + bitIndexOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Bit/BtsRm32ImmHandler.cs b/X86Disassembler/X86/Handlers/Bit/BtsRm32ImmHandler.cs new file mode 100644 index 0000000..b1f08f7 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Bit/BtsRm32ImmHandler.cs @@ -0,0 +1,101 @@ +namespace X86Disassembler.X86.Handlers.Bit; + +using Operands; + +/// +/// Handler for BTS r/m32, imm8 instruction (0F BA /5) +/// +public class BtsRm32ImmHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the BtsRm32ImmHandler class + /// + /// The instruction decoder that owns this handler + public BtsRm32ImmHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // BTS r/m32, imm8 is encoded as 0F BA /5 + if (opcode != 0x0F) + { + return false; + } + + // Check if we have enough bytes to read the second opcode byte + if (!Decoder.CanRead(2)) + { + return false; + } + + var (secondByte, modRm) = Decoder.PeakTwoBytes(); + + // Check if the second byte is BA + if (secondByte != 0xBA) + { + return false; + } + + // Check if the reg field of the ModR/M byte is 5 (BTS) + var reg = ModRMDecoder.GetRegFromModRM(modRm); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 5 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a BTS r/m32, imm8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Bts; + + // Read the second opcode byte (BA) + Decoder.ReadByte(); + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For BTS r/m32, imm8 (0F BA /5): + // - The r/m field with mod specifies the destination operand (register or memory) + // - The immediate value specifies the bit index + var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM(); + + // Check if we have enough bytes for the immediate value + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the immediate byte for the bit position + byte imm8 = Decoder.ReadByte(); + + // Create the immediate operand + var bitIndexOperand = OperandFactory.CreateImmediateOperand(imm8, 8); + + // Set the structured operands + instruction.StructuredOperands = + [ + destinationOperand, + bitIndexOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Call/CallFarPtrHandler.cs b/X86Disassembler/X86/Handlers/Call/CallFarPtrHandler.cs new file mode 100644 index 0000000..da4af0b --- /dev/null +++ b/X86Disassembler/X86/Handlers/Call/CallFarPtrHandler.cs @@ -0,0 +1,94 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Call; + +/// +/// Handler for CALL m16:32 instruction (FF /3) - Far call with memory operand +/// +public class CallFarPtrHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the CallFarPtrHandler class + /// + /// The instruction decoder that owns this handler + public CallFarPtrHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // CALL m16:32 is encoded as FF /3 + if (opcode != 0xFF) + { + return false; + } + + // Check if we have enough bytes to read the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Extract the reg field (bits 3-5) + var reg = ModRMDecoder.PeakModRMReg(); + + // CALL m16:32 is encoded as FF /3 (reg field = 3) + return reg == 3; + } + + /// + /// Decodes a CALL m16:32 instruction (far call) + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Call; + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For CALL m16:32 (FF /3): + // - The r/m field with mod specifies the memory operand + // - This instruction can only reference memory, not registers + var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM(); + + // For far calls, we need to ensure this is a memory operand, not a register + // If mod == 3, then it's a register operand, which is invalid for far calls + if (mod == 3) + { + return false; + } + + // Create a special far pointer operand by modifying the memory operand + // to indicate it's a far pointer (fword ptr) + // We need to ensure the operand is a memory operand before converting it + if (!(operand is MemoryOperand memOperand)) + { + return false; + } + + var farPtrOperand = OperandFactory.CreateFarPointerOperand(memOperand); + + // Set the structured operands + // CALL has only one operand + instruction.StructuredOperands = + [ + farPtrOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Call/CallRm32Handler.cs b/X86Disassembler/X86/Handlers/Call/CallRm32Handler.cs index 95f04f3..4c9ad74 100644 --- a/X86Disassembler/X86/Handlers/Call/CallRm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Call/CallRm32Handler.cs @@ -39,7 +39,9 @@ public class CallRm32Handler : InstructionHandler var reg = ModRMDecoder.PeakModRMReg(); // CALL r/m32 is encoded as FF /2 (reg field = 2) - return reg == 2; + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 2 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Cmp/CmpR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Cmp/CmpR32Rm32Handler.cs index cf1fa3d..0fc4527 100644 --- a/X86Disassembler/X86/Handlers/Cmp/CmpR32Rm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Cmp/CmpR32Rm32Handler.cs @@ -23,7 +23,9 @@ public class CmpR32Rm32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x3B; + // Only handle opcode 0x3B when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x3B && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Cmp/CmpRm32R32Handler.cs b/X86Disassembler/X86/Handlers/Cmp/CmpRm32R32Handler.cs index 99905c9..9ba37eb 100644 --- a/X86Disassembler/X86/Handlers/Cmp/CmpRm32R32Handler.cs +++ b/X86Disassembler/X86/Handlers/Cmp/CmpRm32R32Handler.cs @@ -23,7 +23,9 @@ public class CmpRm32R32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x39; + // Only handle opcode 0x39 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x39 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Dec/DecRegHandler.cs b/X86Disassembler/X86/Handlers/Dec/DecRegHandler.cs index 085db94..28012ba 100644 --- a/X86Disassembler/X86/Handlers/Dec/DecRegHandler.cs +++ b/X86Disassembler/X86/Handlers/Dec/DecRegHandler.cs @@ -24,7 +24,9 @@ public class DecRegHandler : InstructionHandler public override bool CanHandle(byte opcode) { // DEC EAX = 0x48, DEC ECX = 0x49, ..., DEC EDI = 0x4F - return opcode >= 0x48 && opcode <= 0x4F; + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode >= 0x48 && opcode <= 0x4F && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Handler.cs new file mode 100644 index 0000000..aa99da3 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Handler.cs @@ -0,0 +1,77 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Imul; + +/// +/// Handler for IMUL r32, r/m32 instruction (0x0F 0xAF /r) +/// +public class ImulR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the ImulR32Rm32Handler class + /// + /// The instruction decoder that owns this handler + public ImulR32Rm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode sequence + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // IMUL r32, r/m32: opcode 0F AF /r + if (opcode != 0x0F) + return false; + + // Check if we can read the second byte + if (!Decoder.CanReadByte()) + return false; + + // Check if the second byte is 0xAF + byte secondByte = Decoder.PeakByte(); + + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return secondByte == 0xAF && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes an IMUL r32, r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + instruction.Type = InstructionType.IMul; + + // Read the second byte of the opcode (0xAF) + if (!Decoder.CanReadByte()) + { + return false; + } + byte secondByte = Decoder.ReadByte(); + if (secondByte != 0xAF) + { + return false; + } + + // Read ModR/M: reg = destination, r/m = source + var (_, reg, _, operand) = ModRMDecoder.ReadModRM(); + + // Create the destination register operand (32-bit) + var destOperand = OperandFactory.CreateRegisterOperand(reg); + + // Source operand is already an Operand + instruction.StructuredOperands = + [ + destOperand, + operand + ]; + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Imm32Handler.cs b/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Imm32Handler.cs new file mode 100644 index 0000000..57a13c1 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Imm32Handler.cs @@ -0,0 +1,57 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Imul; + +/// +/// Handler for IMUL r32, r/m32, imm32 instruction (0x69 /r id) +/// +public class ImulR32Rm32Imm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the ImulR32Rm32Imm32Handler class + /// + /// The instruction decoder that owns this handler + public ImulR32Rm32Imm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // IMUL r32, r/m32, imm32: opcode 69 /r id + return opcode == 0x69; + } + + /// + /// Decodes an IMUL r32, r/m32, imm32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + instruction.Type = InstructionType.IMul; + + // Read ModR/M: reg = destination, r/m = source + var (_, reg, _, operand) = ModRMDecoder.ReadModRM(); + + var destOperand = OperandFactory.CreateRegisterOperand(reg); + + // Read imm32 (4 bytes) + uint imm32 = Decoder.ReadUInt32(); + var immOperand = OperandFactory.CreateImmediateOperand(imm32); + + instruction.StructuredOperands = + [ + destOperand, + operand, + immOperand + ]; + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Imm8Handler.cs b/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Imm8Handler.cs new file mode 100644 index 0000000..158a1fd --- /dev/null +++ b/X86Disassembler/X86/Handlers/Imul/ImulR32Rm32Imm8Handler.cs @@ -0,0 +1,57 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Imul; + +/// +/// Handler for IMUL r32, r/m32, imm8 instruction (0x6B /r ib) +/// +public class ImulR32Rm32Imm8Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the ImulR32Rm32Imm8Handler class + /// + /// The instruction decoder that owns this handler + public ImulR32Rm32Imm8Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // IMUL r32, r/m32, imm8: opcode 6B /r ib + return opcode == 0x6B; + } + + /// + /// Decodes an IMUL r32, r/m32, imm8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + instruction.Type = InstructionType.IMul; + + // Read ModR/M: reg = destination, r/m = source + var (_, reg, _, operand) = ModRMDecoder.ReadModRM(); + + var destOperand = OperandFactory.CreateRegisterOperand(reg); + + // Read imm8 and sign-extend to int32 + sbyte imm8 = (sbyte)Decoder.ReadByte(); + var immOperand = OperandFactory.CreateImmediateOperand((uint)imm8, 8); // 8-bit immediate, sign-extended + + instruction.StructuredOperands = + [ + destOperand, + operand, + immOperand + ]; + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Inc/IncRegHandler.cs b/X86Disassembler/X86/Handlers/Inc/IncRegHandler.cs index 1bee3f2..1227fe3 100644 --- a/X86Disassembler/X86/Handlers/Inc/IncRegHandler.cs +++ b/X86Disassembler/X86/Handlers/Inc/IncRegHandler.cs @@ -24,7 +24,9 @@ public class IncRegHandler : InstructionHandler public override bool CanHandle(byte opcode) { // INC EAX = 0x40, INC ECX = 0x41, ..., INC EDI = 0x47 - return opcode >= 0x40 && opcode <= 0x47; + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode >= 0x40 && opcode <= 0x47 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs index 2993b97..2fca9b4 100644 --- a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs +++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs @@ -1,6 +1,7 @@ using X86Disassembler.X86.Handlers.Adc; using X86Disassembler.X86.Handlers.Add; using X86Disassembler.X86.Handlers.And; +using X86Disassembler.X86.Handlers.Bit; using X86Disassembler.X86.Handlers.Call; using X86Disassembler.X86.Handlers.Cmp; using X86Disassembler.X86.Handlers.Dec; @@ -63,7 +64,8 @@ public class InstructionHandlerFactory _handlers.Add(new Int3Handler(_decoder)); // Register handlers in order of priority (most specific first) - RegisterArithmeticImmediateHandlers(); // Group 1 instructions (including 0x83) + RegisterSbbHandlers(); // SBB instructions + RegisterAdcHandlers(); // ADC instructions RegisterAddHandlers(); // ADD instructions RegisterAndHandlers(); // AND instructions RegisterOrHandlers(); // OR instructions @@ -72,7 +74,6 @@ public class InstructionHandlerFactory RegisterTestHandlers(); // TEST instructions // Register arithmetic unary instructions - RegisterArithmeticUnaryHandlers(); // Empty, kept for consistency RegisterNotHandlers(); // NOT instructions RegisterNegHandlers(); // NEG instructions RegisterMulHandlers(); // MUL instructions @@ -93,32 +94,44 @@ public class InstructionHandlerFactory RegisterMovHandlers(); RegisterSubHandlers(); // Register SUB handlers RegisterNopHandlers(); // Register NOP handlers + RegisterBitHandlers(); // Register bit manipulation handlers } /// - /// Registers all ArithmeticUnary instruction handlers + /// Registers all SBB instruction handlers /// - private void RegisterArithmeticUnaryHandlers() + private void RegisterSbbHandlers() { - // This method is kept for consistency, but all handlers have been moved to their own namespaces - } - - /// - /// Registers all ArithmeticImmediate instruction handlers - /// - private void RegisterArithmeticImmediateHandlers() - { - // ADC handlers - _handlers.Add(new AdcImmToRm32Handler(_decoder)); // ADC r/m32, imm32 (opcode 81 /2) - _handlers.Add(new AdcImmToRm32SignExtendedHandler(_decoder)); // ADC r/m32, imm8 (opcode 83 /2) - - // SBB handlers + // SBB immediate handlers _handlers.Add(new SbbImmFromRm32Handler(_decoder)); // SBB r/m32, imm32 (opcode 81 /3) _handlers.Add(new SbbImmFromRm32SignExtendedHandler(_decoder)); // SBB r/m32, imm8 (opcode 83 /3) + } + + /// + /// Registers all ADC instruction handlers + /// + private void RegisterAdcHandlers() + { + // ADC immediate handlers + _handlers.Add(new AdcImmToRm8Handler(_decoder)); // ADC r/m8, imm8 (opcode 80 /2) + _handlers.Add(new AdcImmToRm16Handler(_decoder)); // ADC r/m16, imm16 (opcode 81 /2 with 0x66 prefix) + _handlers.Add(new AdcImmToRm16SignExtendedHandler(_decoder)); // ADC r/m16, imm8 (opcode 83 /2 with 0x66 prefix) + _handlers.Add(new AdcImmToRm32Handler(_decoder)); // ADC r/m32, imm32 (opcode 81 /2) + _handlers.Add(new AdcImmToRm32SignExtendedHandler(_decoder)); // ADC r/m32, imm8 (opcode 83 /2) + _handlers.Add(new AdcAlImmHandler(_decoder)); // ADC AL, imm8 (opcode 14) + _handlers.Add(new AdcAccumulatorImmHandler(_decoder)); // ADC AX/EAX, imm16/32 (opcode 15) - // SUB handlers - _handlers.Add(new SubImmFromRm32Handler(_decoder)); // SUB r/m32, imm32 (opcode 81 /5) - _handlers.Add(new SubImmFromRm32SignExtendedHandler(_decoder)); // SUB r/m32, imm8 (opcode 83 /5) + // Register-to-register ADC handlers (8-bit) + _handlers.Add(new AdcR8Rm8Handler(_decoder)); // ADC r8, r/m8 (opcode 12) + _handlers.Add(new AdcRm8R8Handler(_decoder)); // ADC r/m8, r8 (opcode 10) + + // Register-to-register ADC handlers (16-bit) + _handlers.Add(new AdcR16Rm16Handler(_decoder)); // ADC r16, r/m16 (opcode 13 with 0x66 prefix) + _handlers.Add(new AdcRm16R16Handler(_decoder)); // ADC r/m16, r16 (opcode 11 with 0x66 prefix) + + // Register-to-register ADC handlers (32-bit) + _handlers.Add(new AdcR32Rm32Handler(_decoder)); // ADC r32, r/m32 (opcode 13) + _handlers.Add(new AdcRm32R32Handler(_decoder)); // ADC r/m32, r32 (opcode 11) } /// @@ -310,14 +323,6 @@ public class InstructionHandlerFactory _handlers.Add(new MovRm32Imm32Handler(_decoder)); _handlers.Add(new MovRm8Imm8Handler(_decoder)); - // Add PUSH handlers - _handlers.Add(new PushRegHandler(_decoder)); - _handlers.Add(new PushImm32Handler(_decoder)); - _handlers.Add(new PushImm8Handler(_decoder)); - - // Add POP handlers - _handlers.Add(new PopRegHandler(_decoder)); - // Add XCHG handlers _handlers.Add(new XchgEaxRegHandler(_decoder)); } @@ -392,6 +397,7 @@ public class InstructionHandlerFactory { // Add POP register handlers _handlers.Add(new PopRegHandler(_decoder)); // POP r32 (opcode 58+r) + _handlers.Add(new PopRm32Handler(_decoder)); // POP r/m32 (opcode 8F /0) } /// @@ -421,20 +427,20 @@ public class InstructionHandlerFactory private void RegisterSubHandlers() { // Register SUB handlers - + + // 16-bit handlers with operand size prefix (must come first) + _handlers.Add(new SubAxImm16Handler(_decoder)); + _handlers.Add(new SubImmFromRm16Handler(_decoder)); + _handlers.Add(new SubImmFromRm16SignExtendedHandler(_decoder)); + _handlers.Add(new SubRm16R16Handler(_decoder)); + _handlers.Add(new SubR16Rm16Handler(_decoder)); + // 32-bit handlers _handlers.Add(new SubRm32R32Handler(_decoder)); _handlers.Add(new SubR32Rm32Handler(_decoder)); _handlers.Add(new SubImmFromRm32Handler(_decoder)); _handlers.Add(new SubImmFromRm32SignExtendedHandler(_decoder)); - // 16-bit handlers - _handlers.Add(new SubRm16R16Handler(_decoder)); - _handlers.Add(new SubR16Rm16Handler(_decoder)); - _handlers.Add(new SubAxImm16Handler(_decoder)); - _handlers.Add(new SubImmFromRm16Handler(_decoder)); - _handlers.Add(new SubImmFromRm16SignExtendedHandler(_decoder)); - // 8-bit handlers _handlers.Add(new SubRm8R8Handler(_decoder)); _handlers.Add(new SubR8Rm8Handler(_decoder)); @@ -453,6 +459,32 @@ public class InstructionHandlerFactory _handlers.Add(new MultiByteNopHandler(_decoder)); } + /// + /// Registers all bit manipulation instruction handlers + /// + private void RegisterBitHandlers() + { + // BT (Bit Test) handlers + _handlers.Add(new BtR32Rm32Handler(_decoder)); // BT r32, r/m32 (0F A3) + _handlers.Add(new BtRm32ImmHandler(_decoder)); // BT r/m32, imm8 (0F BA /4) + + // BTS (Bit Test and Set) handlers + _handlers.Add(new BtsR32Rm32Handler(_decoder)); // BTS r32, r/m32 (0F AB) + _handlers.Add(new BtsRm32ImmHandler(_decoder)); // BTS r/m32, imm8 (0F BA /5) + + // BTR (Bit Test and Reset) handlers + _handlers.Add(new BtrR32Rm32Handler(_decoder)); // BTR r32, r/m32 (0F B3) + _handlers.Add(new BtrRm32ImmHandler(_decoder)); // BTR r/m32, imm8 (0F BA /6) + + // BTC (Bit Test and Complement) handlers + _handlers.Add(new BtcR32Rm32Handler(_decoder)); // BTC r32, r/m32 (0F BB) + _handlers.Add(new BtcRm32ImmHandler(_decoder)); // BTC r/m32, imm8 (0F BA /7) + + // BSF and BSR (Bit Scan) handlers + _handlers.Add(new BsfR32Rm32Handler(_decoder)); // BSF r32, r/m32 (0F BC) + _handlers.Add(new BsrR32Rm32Handler(_decoder)); // BSR r32, r/m32 (0F BD) + } + /// /// Registers all NEG instruction handlers /// diff --git a/X86Disassembler/X86/Handlers/Jump/JmpRel32Handler.cs b/X86Disassembler/X86/Handlers/Jump/JmpRel32Handler.cs index 37253bf..5b15c7e 100644 --- a/X86Disassembler/X86/Handlers/Jump/JmpRel32Handler.cs +++ b/X86Disassembler/X86/Handlers/Jump/JmpRel32Handler.cs @@ -23,7 +23,9 @@ public class JmpRel32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0xE9; + // Only handle opcode 0xE9 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0xE9 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Jump/JmpRm32Handler.cs b/X86Disassembler/X86/Handlers/Jump/JmpRm32Handler.cs index 0bbaee0..bd24c7f 100644 --- a/X86Disassembler/X86/Handlers/Jump/JmpRm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Jump/JmpRm32Handler.cs @@ -39,7 +39,9 @@ public class JmpRm32Handler : InstructionHandler var reg = ModRMDecoder.PeakModRMReg(); // JMP r/m32 is encoded as FF /4 (reg field = 4) - return reg == 4; + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 4 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Lea/LeaR32MHandler.cs b/X86Disassembler/X86/Handlers/Lea/LeaR32MHandler.cs index dda70df..eaadf84 100644 --- a/X86Disassembler/X86/Handlers/Lea/LeaR32MHandler.cs +++ b/X86Disassembler/X86/Handlers/Lea/LeaR32MHandler.cs @@ -23,7 +23,9 @@ public class LeaR32MHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x8D; + // Only handle opcode 0x8D when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x8D && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Mov/MovMemRegHandler.cs b/X86Disassembler/X86/Handlers/Mov/MovMemRegHandler.cs index 4a68a9a..476df3d 100644 --- a/X86Disassembler/X86/Handlers/Mov/MovMemRegHandler.cs +++ b/X86Disassembler/X86/Handlers/Mov/MovMemRegHandler.cs @@ -23,7 +23,13 @@ public class MovMemRegHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x88 || opcode == 0x89; + // For 8-bit operations (0x88), no prefix check needed + if (opcode == 0x88) + return true; + + // For 32-bit operations (0x89), only handle when operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x89 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Mov/MovRegMemHandler.cs b/X86Disassembler/X86/Handlers/Mov/MovRegMemHandler.cs index c6a65bc..7f7d6dd 100644 --- a/X86Disassembler/X86/Handlers/Mov/MovRegMemHandler.cs +++ b/X86Disassembler/X86/Handlers/Mov/MovRegMemHandler.cs @@ -23,7 +23,13 @@ public class MovRegMemHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x8A || opcode == 0x8B; + // For 8-bit operations (0x8A), no prefix check needed + if (opcode == 0x8A) + return true; + + // For 32-bit operations (0x8B), only handle when operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x8B && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Mov/MovRm32Imm32Handler.cs b/X86Disassembler/X86/Handlers/Mov/MovRm32Imm32Handler.cs index 899737c..ef77eb3 100644 --- a/X86Disassembler/X86/Handlers/Mov/MovRm32Imm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Mov/MovRm32Imm32Handler.cs @@ -34,7 +34,7 @@ public class MovRm32Imm32Handler : InstructionHandler return false; } - // Peek at the ModR/M byte without advancing the position + // Peak at the ModR/M byte without advancing the position var reg = ModRMDecoder.PeakModRMReg(); // MOV r/m8, imm8 only uses reg=0 diff --git a/X86Disassembler/X86/Handlers/Mov/MovRm8Imm8Handler.cs b/X86Disassembler/X86/Handlers/Mov/MovRm8Imm8Handler.cs index dabfcf8..0f58806 100644 --- a/X86Disassembler/X86/Handlers/Mov/MovRm8Imm8Handler.cs +++ b/X86Disassembler/X86/Handlers/Mov/MovRm8Imm8Handler.cs @@ -35,7 +35,7 @@ public class MovRm8Imm8Handler : InstructionHandler return false; } - // Peek at the ModR/M byte without advancing the position + // Peak at the ModR/M byte without advancing the position var reg = ModRMDecoder.PeakModRMReg(); // MOV r/m8, imm8 only uses reg=0 diff --git a/X86Disassembler/X86/Handlers/Neg/NegRm32Handler.cs b/X86Disassembler/X86/Handlers/Neg/NegRm32Handler.cs index cc44618..901596b 100644 --- a/X86Disassembler/X86/Handlers/Neg/NegRm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Neg/NegRm32Handler.cs @@ -32,7 +32,9 @@ public class NegRm32Handler : InstructionHandler var reg = ModRMDecoder.PeakModRMReg(); - return reg == 3; // 3 = NEG + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 3 && !Decoder.HasOperandSizePrefix(); // 3 = NEG } /// diff --git a/X86Disassembler/X86/Handlers/Not/NotRm32Handler.cs b/X86Disassembler/X86/Handlers/Not/NotRm32Handler.cs index bb7cdb8..5c0a8c0 100644 --- a/X86Disassembler/X86/Handlers/Not/NotRm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Not/NotRm32Handler.cs @@ -33,7 +33,9 @@ public class NotRm32Handler : InstructionHandler var reg = ModRMDecoder.PeakModRMReg(); - return reg == 2; // 2 = NOT + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 2 && !Decoder.HasOperandSizePrefix(); // 2 = NOT } /// @@ -55,7 +57,7 @@ public class NotRm32Handler : InstructionHandler // Read the ModR/M byte // For NOT r/m32 (0xF7 /2): // - The r/m field with mod specifies the operand (register or memory) - var (_, reg, _, operand) = ModRMDecoder.ReadModRM(); + var (_, _, _, operand) = ModRMDecoder.ReadModRM(); // Set the structured operands // NOT has only one operand diff --git a/X86Disassembler/X86/Handlers/Not/NotRm8Handler.cs b/X86Disassembler/X86/Handlers/Not/NotRm8Handler.cs index b882b40..c17c2b2 100644 --- a/X86Disassembler/X86/Handlers/Not/NotRm8Handler.cs +++ b/X86Disassembler/X86/Handlers/Not/NotRm8Handler.cs @@ -55,7 +55,7 @@ public class NotRm8Handler : InstructionHandler // Read the ModR/M byte // For NOT r/m8 (0xF6 /2): // - The r/m field with mod specifies the operand (register or memory) - var (_, reg, _, operand) = ModRMDecoder.ReadModRM8(); + var (_, _, _, operand) = ModRMDecoder.ReadModRM8(); // Set the structured operands // NOT has only one operand diff --git a/X86Disassembler/X86/Handlers/Or/OrR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Or/OrR32Rm32Handler.cs index 8e6fbd0..72edc4f 100644 --- a/X86Disassembler/X86/Handlers/Or/OrR32Rm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Or/OrR32Rm32Handler.cs @@ -23,7 +23,9 @@ public class OrR32Rm32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x0B; + // Only handle opcode 0x0B when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x0B && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Or/OrRm32R32Handler.cs b/X86Disassembler/X86/Handlers/Or/OrRm32R32Handler.cs index 3d42e31..300206a 100644 --- a/X86Disassembler/X86/Handlers/Or/OrRm32R32Handler.cs +++ b/X86Disassembler/X86/Handlers/Or/OrRm32R32Handler.cs @@ -23,7 +23,9 @@ public class OrRm32R32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x09; + // Only handle opcode 0x09 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x09 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Pop/PopRegHandler.cs b/X86Disassembler/X86/Handlers/Pop/PopRegHandler.cs index e3ab59d..7cea6a3 100644 --- a/X86Disassembler/X86/Handlers/Pop/PopRegHandler.cs +++ b/X86Disassembler/X86/Handlers/Pop/PopRegHandler.cs @@ -23,7 +23,9 @@ public class PopRegHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode >= 0x58 && opcode <= 0x5F; + // Only handle opcodes 0x58-0x5F when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode >= 0x58 && opcode <= 0x5F && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Pop/PopRm32Handler.cs b/X86Disassembler/X86/Handlers/Pop/PopRm32Handler.cs new file mode 100644 index 0000000..ae09301 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Pop/PopRm32Handler.cs @@ -0,0 +1,77 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.Pop; + +/// +/// Handler for POP r/m32 instruction (0x8F /0) +/// +public class PopRm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the PopRm32Handler class + /// + /// The instruction decoder that owns this handler + public PopRm32Handler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // POP r/m32 is encoded as 8F /0 + if (opcode != 0x8F) + { + return false; + } + + // Check if we have enough bytes to read the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + var reg = ModRMDecoder.PeakModRMReg(); + + // POP r/m32 is encoded as 8F /0 (reg field = 0) + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 0 && !Decoder.HasOperandSizePrefix(); + } + + /// + /// Decodes a POP r/m32 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the instruction type + instruction.Type = InstructionType.Pop; + + // Check if we have enough bytes for the ModR/M byte + if (!Decoder.CanReadByte()) + { + return false; + } + + // Read the ModR/M byte + // For POP r/m32 (8F /0): + // - The r/m field with mod specifies the operand (register or memory) + var (_, _, _, operand) = ModRMDecoder.ReadModRM(); + + // Set the structured operands + // POP has only one operand + instruction.StructuredOperands = + [ + operand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Push/PushRm32Handler.cs b/X86Disassembler/X86/Handlers/Push/PushRm32Handler.cs index 8a9a5d0..51301be 100644 --- a/X86Disassembler/X86/Handlers/Push/PushRm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Push/PushRm32Handler.cs @@ -38,7 +38,9 @@ public class PushRm32Handler : InstructionHandler var reg = ModRMDecoder.PeakModRMReg(); // PUSH r/m32 is encoded as FF /6 (reg field = 6) - return reg == 6; + // Only handle when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return reg == 6 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Ret/RetHandler.cs b/X86Disassembler/X86/Handlers/Ret/RetHandler.cs index f89478d..b985719 100644 --- a/X86Disassembler/X86/Handlers/Ret/RetHandler.cs +++ b/X86Disassembler/X86/Handlers/Ret/RetHandler.cs @@ -23,7 +23,9 @@ public class RetHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0xC3; + // Only handle opcode 0xC3 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0xC3 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Ret/RetImmHandler.cs b/X86Disassembler/X86/Handlers/Ret/RetImmHandler.cs index 1f5b9fa..e5ff54e 100644 --- a/X86Disassembler/X86/Handlers/Ret/RetImmHandler.cs +++ b/X86Disassembler/X86/Handlers/Ret/RetImmHandler.cs @@ -23,7 +23,9 @@ public class RetImmHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0xC2; + // Only handle opcode 0xC2 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0xC2 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Sub/SubAlImm8Handler.cs b/X86Disassembler/X86/Handlers/Sub/SubAlImm8Handler.cs index 624e192..09d8af3 100644 --- a/X86Disassembler/X86/Handlers/Sub/SubAlImm8Handler.cs +++ b/X86Disassembler/X86/Handlers/Sub/SubAlImm8Handler.cs @@ -46,7 +46,7 @@ public class SubAlImm8Handler : InstructionHandler instruction.Type = InstructionType.Sub; // Create the destination register operand (AL) - var destinationOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, 8); + var destinationOperand = OperandFactory.CreateRegisterOperand8(RegisterIndex8.AL); // Create the source immediate operand var sourceOperand = OperandFactory.CreateImmediateOperand(imm8, 8); diff --git a/X86Disassembler/X86/Handlers/Sub/SubR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Sub/SubR32Rm32Handler.cs index c1ec14e..f7e1ea3 100644 --- a/X86Disassembler/X86/Handlers/Sub/SubR32Rm32Handler.cs +++ b/X86Disassembler/X86/Handlers/Sub/SubR32Rm32Handler.cs @@ -23,7 +23,9 @@ public class SubR32Rm32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x2B; + // Only handle opcode 0x2B when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x2B && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Sub/SubRm32R32Handler.cs b/X86Disassembler/X86/Handlers/Sub/SubRm32R32Handler.cs index 58ff242..44c7a52 100644 --- a/X86Disassembler/X86/Handlers/Sub/SubRm32R32Handler.cs +++ b/X86Disassembler/X86/Handlers/Sub/SubRm32R32Handler.cs @@ -23,7 +23,9 @@ public class SubRm32R32Handler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x29; + // Only handle opcode 0x29 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x29 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Test/TestRegMemHandler.cs b/X86Disassembler/X86/Handlers/Test/TestRegMemHandler.cs index 73998e1..732df16 100644 --- a/X86Disassembler/X86/Handlers/Test/TestRegMemHandler.cs +++ b/X86Disassembler/X86/Handlers/Test/TestRegMemHandler.cs @@ -23,7 +23,9 @@ public class TestRegMemHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x85; + // Only handle opcode 0x85 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x85 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Xchg/XchgEaxRegHandler.cs b/X86Disassembler/X86/Handlers/Xchg/XchgEaxRegHandler.cs index 32ea7ea..b97651e 100644 --- a/X86Disassembler/X86/Handlers/Xchg/XchgEaxRegHandler.cs +++ b/X86Disassembler/X86/Handlers/Xchg/XchgEaxRegHandler.cs @@ -23,40 +23,9 @@ public class XchgEaxRegHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode >= 0x91 && opcode <= 0x97; - } - - /// - /// Maps the register index from the opcode to the RegisterIndex enum value expected by tests - /// - /// The register index from the opcode (0-7) - /// The corresponding RegisterIndex enum value - private RegisterIndex MapOpcodeToRegisterIndex(int opcodeRegIndex) - { - // The mapping from opcode register index to RegisterIndex enum is: - // 0 -> A (EAX) - // 1 -> C (ECX) - // 2 -> D (EDX) - // 3 -> B (EBX) - // 4 -> Sp (ESP) - // 5 -> Bp (EBP) - // 6 -> Si (ESI) - // 7 -> Di (EDI) - - // This mapping is based on the x86 instruction encoding - // but we need to map to the RegisterIndex enum values that the tests expect - return opcodeRegIndex switch - { - 0 => RegisterIndex.A, // EAX - 1 => RegisterIndex.C, // ECX - 2 => RegisterIndex.D, // EDX - 3 => RegisterIndex.B, // EBX - 4 => RegisterIndex.Sp, // ESP - 5 => RegisterIndex.Bp, // EBP - 6 => RegisterIndex.Si, // ESI - 7 => RegisterIndex.Di, // EDI - _ => RegisterIndex.A // Default case, should never happen - }; + // Only handle opcodes 0x91-0x97 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode >= 0x91 && opcode <= 0x97 && !Decoder.HasOperandSizePrefix(); } /// @@ -71,10 +40,7 @@ public class XchgEaxRegHandler : InstructionHandler instruction.Type = InstructionType.Xchg; // Register is encoded in the low 3 bits of the opcode - int opcodeRegIndex = opcode & 0x07; - - // Map the opcode register index to the RegisterIndex enum value - RegisterIndex reg = MapOpcodeToRegisterIndex(opcodeRegIndex); + RegisterIndex reg = (RegisterIndex)(opcode & 0x07); // Create the register operands var eaxOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A); diff --git a/X86Disassembler/X86/Handlers/Xor/XorAlImmHandler.cs b/X86Disassembler/X86/Handlers/Xor/XorAlImmHandler.cs index ed26ecb..94d3395 100644 --- a/X86Disassembler/X86/Handlers/Xor/XorAlImmHandler.cs +++ b/X86Disassembler/X86/Handlers/Xor/XorAlImmHandler.cs @@ -46,7 +46,7 @@ public class XorAlImmHandler : InstructionHandler byte imm8 = Decoder.ReadByte(); // Create the register operand for AL - var alOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, 8); + var alOperand = OperandFactory.CreateRegisterOperand8(RegisterIndex8.AL); // Create the immediate operand var immOperand = OperandFactory.CreateImmediateOperand(imm8, 8); diff --git a/X86Disassembler/X86/Handlers/Xor/XorMemRegHandler.cs b/X86Disassembler/X86/Handlers/Xor/XorMemRegHandler.cs index f19c75f..adc0335 100644 --- a/X86Disassembler/X86/Handlers/Xor/XorMemRegHandler.cs +++ b/X86Disassembler/X86/Handlers/Xor/XorMemRegHandler.cs @@ -23,7 +23,9 @@ public class XorMemRegHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x31; + // Only handle opcode 0x31 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x31 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/Handlers/Xor/XorRegMemHandler.cs b/X86Disassembler/X86/Handlers/Xor/XorRegMemHandler.cs index 4a7958a..cd3825b 100644 --- a/X86Disassembler/X86/Handlers/Xor/XorRegMemHandler.cs +++ b/X86Disassembler/X86/Handlers/Xor/XorRegMemHandler.cs @@ -25,7 +25,9 @@ public class XorRegMemHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - return opcode == 0x33; + // Only handle opcode 0x33 when the operand size prefix is NOT present + // This ensures 16-bit handlers get priority when the prefix is present + return opcode == 0x33 && !Decoder.HasOperandSizePrefix(); } /// diff --git a/X86Disassembler/X86/InstructionDecoder.cs b/X86Disassembler/X86/InstructionDecoder.cs index b1465f0..8c06e67 100644 --- a/X86Disassembler/X86/InstructionDecoder.cs +++ b/X86Disassembler/X86/InstructionDecoder.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Diagnostics.Contracts; namespace X86Disassembler.X86; @@ -6,6 +5,11 @@ namespace X86Disassembler.X86; using Handlers; using Operands; +public static class Printer +{ + public static Action? WriteLine; +} + /// /// Decodes x86 instructions from a byte buffer /// @@ -83,44 +87,19 @@ public class InstructionDecoder } } - // If only prefixes were found, return a prefix-only instruction + // If only prefixes were found and we're at the end of the buffer, return null if (_position > startPosition && !CanReadByte()) - { - // Check for segment override prefix - if (_prefixDecoder.HasSegmentOverridePrefix()) - { - // Set the instruction type to Rep for segment override prefixes when they appear alone - // This matches the expected behavior in the tests - instruction.Type = InstructionType.Rep; - } - else - { - // Set the instruction type to Unknown for other prefixes - instruction.Type = InstructionType.Unknown; - } - - // Add segment override prefix as an operand if present - string segmentOverride = _prefixDecoder.GetSegmentOverride(); - if (!string.IsNullOrEmpty(segmentOverride)) - { - // Could create a special operand for segment overrides if needed - } - - return instruction; - } - - if (!CanReadByte()) { return null; } - + // Read the opcode byte opcode = ReadByte(); // Get a handler for the opcode var handler = _handlerFactory.GetHandler(opcode); - Debug.WriteLine($"Resolved handler {handler?.GetType().Name}"); + Printer.WriteLine?.Invoke($"Resolved handler {handler?.GetType().Name}"); bool handlerSuccess = false; @@ -326,6 +305,20 @@ public class InstructionDecoder return _codeBuffer[_position]; } + /// + /// Peaks a byte from the buffer without adjusting position + /// + /// The byte peaked + public (byte b1, byte b2) PeakTwoBytes() + { + if (_position + 1 >= _length) + { + return (0,0); + } + + return (_codeBuffer[_position], _codeBuffer[_position + 1]); + } + /// /// Peaks a byte from the buffer at the specified offset from current position without adjusting position /// diff --git a/X86Disassembler/X86/ModRMDecoder.cs b/X86Disassembler/X86/ModRMDecoder.cs index a7b11cf..b82da19 100644 --- a/X86Disassembler/X86/ModRMDecoder.cs +++ b/X86Disassembler/X86/ModRMDecoder.cs @@ -185,6 +185,18 @@ public class ModRMDecoder return regIndex; } + /// + /// Extracts modRM reg field + /// + /// A reg from the ModR/M byte + public static byte GetRegFromModRM(byte modRm) + { + // Extract fields from ModR/M byte + byte regIndex = (byte)((modRm & Constants.REG_MASK) >> 3); // Middle 3 bits (bits 3-5) + + return regIndex; + } + /// /// Reads and decodes a ModR/M byte for standard 32-bit operands /// diff --git a/X86Disassembler/X86/Operands/DisplacementMemoryOperand.cs b/X86Disassembler/X86/Operands/DisplacementMemoryOperand.cs index 9a43955..5390ab4 100644 --- a/X86Disassembler/X86/Operands/DisplacementMemoryOperand.cs +++ b/X86Disassembler/X86/Operands/DisplacementMemoryOperand.cs @@ -39,26 +39,28 @@ public class DisplacementMemoryOperand : MemoryOperand var registerName = RegisterMapper.GetRegisterName(BaseRegister, 32); // Format the displacement value - string formattedDisplacement; - string sign; - - // Handle positive and negative displacements - if (Displacement >= 0) + long absDisplacement = Math.Abs(Displacement); + string sign = Displacement >= 0 ? "+" : "-"; + string format; + + if (absDisplacement == 0) { - sign = "+"; - formattedDisplacement = Displacement < 256 - ? $"0x{Displacement:X2}" - : $"0x{Displacement:X8}"; + format = "X2"; + } + else if (absDisplacement <= 0xFF) + { + format = "X2"; + } + else if (absDisplacement <= 0xFFFF) + { + format = "X4"; } else { - sign = "-"; - // For negative values, take the absolute value for display - var absDisplacement = Math.Abs(Displacement); - formattedDisplacement = absDisplacement < 256 - ? $"0x{absDisplacement:X2}" - : $"0x{absDisplacement:X8}"; + format = "X8"; } + + string formattedDisplacement = $"0x{absDisplacement.ToString(format)}"; return $"{GetSizePrefix()}[{registerName}{sign}{formattedDisplacement}]"; } diff --git a/X86Disassembler/X86/Operands/ImmediateOperand.cs b/X86Disassembler/X86/Operands/ImmediateOperand.cs index 605e78d..ac48a75 100644 --- a/X86Disassembler/X86/Operands/ImmediateOperand.cs +++ b/X86Disassembler/X86/Operands/ImmediateOperand.cs @@ -46,42 +46,25 @@ public class ImmediateOperand : Operand _ => Value }; - // For 8-bit immediate values, always use at least 2 digits - if (Size == 8) + string format; + + if (maskedValue == 0) { - return $"0x{maskedValue:X2}"; + format = "X2"; } - - // For 16-bit immediate values, format depends on the value - if (Size == 16) + else if (maskedValue <= 0xFF) { - // For small values (< 256), show without leading zeros - if (maskedValue < 256) - { - return $"0x{maskedValue:X}"; - } - - // For larger values, use at least 4 digits - return $"0x{maskedValue:X4}"; + format = "X2"; } - - // For 32-bit immediate values, format depends on the instruction context - if (Size == 32) + else if (maskedValue <= 0xFFFF) { - // For small values (0), always show as 0x00 - if (maskedValue == 0) - { - return "0x00"; - } - - // For other small values (< 256), show as 0xNN - if (maskedValue < 256) - { - return $"0x{maskedValue:X2}"; - } + format = "X4"; } - - // For larger 32-bit values, show the full 32-bit representation - return $"0x{maskedValue:X8}"; + else + { + format = "X8"; + } + + return $"0x{maskedValue.ToString(format)}"; } } diff --git a/X86DisassemblerTests/InstructionTests/DataTransferInstructionTests.cs b/X86DisassemblerTests/InstructionTests/DataTransferInstructionTests.cs index 1529fe9..dd078f6 100644 --- a/X86DisassemblerTests/InstructionTests/DataTransferInstructionTests.cs +++ b/X86DisassemblerTests/InstructionTests/DataTransferInstructionTests.cs @@ -148,9 +148,9 @@ public class DataTransferInstructionTests // Check the first operand (AL) var alOperand = instruction.StructuredOperands[0]; - Assert.IsType(alOperand); - var alRegisterOperand = (RegisterOperand)alOperand; - Assert.Equal(RegisterIndex.A, alRegisterOperand.Register); + Assert.IsType(alOperand); + var alRegisterOperand = (Register8Operand)alOperand; + Assert.Equal(RegisterIndex8.AL, alRegisterOperand.Register); Assert.Equal(8, alRegisterOperand.Size); // Validate that it's an 8-bit register (AL) // Check the second operand (Immediate) diff --git a/X86DisassemblerTests/InstructionTests/PushInstructionTests.cs b/X86DisassemblerTests/InstructionTests/PushInstructionTests.cs new file mode 100644 index 0000000..78b0c3b --- /dev/null +++ b/X86DisassemblerTests/InstructionTests/PushInstructionTests.cs @@ -0,0 +1,97 @@ +using X86Disassembler.X86; +using X86Disassembler.X86.Operands; + +namespace X86DisassemblerTests.InstructionTests; + +/// +/// Tests for PUSH instruction handlers +/// +public class PushInstructionTests +{ + /// + /// Tests the PUSH imm32 instruction (0x68) + /// + [Fact] + public void TestPushImm32() + { + // Arrange + byte[] code = { 0x68, 0x78, 0x56, 0x34, 0x12 }; // PUSH 0x12345678 + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + var instruction = instructions[0]; + Assert.Equal(InstructionType.Push, instruction.Type); + + // Check that we have one operand + Assert.Single(instruction.StructuredOperands); + + // Check the operand (immediate value) + var operand = instruction.StructuredOperands[0]; + Assert.IsType(operand); + var immOperand = (ImmediateOperand)operand; + Assert.Equal(0x12345678u, immOperand.Value); + Assert.Equal(32, immOperand.Size); + } + + /// + /// Tests the PUSH imm16 instruction with operand size prefix (0x66 0x68) + /// + [Fact] + public void TestPushImm16WithOperandSizePrefix() + { + // Arrange + byte[] code = { 0x66, 0x68, 0x78, 0x56 }; // PUSH 0x5678 (with operand size prefix) + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + var instruction = instructions[0]; + Assert.Equal(InstructionType.Push, instruction.Type); + + // Check that we have one operand + Assert.Single(instruction.StructuredOperands); + + // Check the operand (immediate value) + var operand = instruction.StructuredOperands[0]; + Assert.IsType(operand); + var immOperand = (ImmediateOperand)operand; + Assert.Equal(0x5678u, immOperand.Value); + Assert.Equal(16, immOperand.Size); + } + + /// + /// Tests the PUSH imm8 instruction (0x6A) + /// + [Fact] + public void TestPushImm8() + { + // Arrange + byte[] code = { 0x6A, 0x42 }; // PUSH 0x42 + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + var instruction = instructions[0]; + Assert.Equal(InstructionType.Push, instruction.Type); + + // Check that we have one operand + Assert.Single(instruction.StructuredOperands); + + // Check the operand (immediate value) + var operand = instruction.StructuredOperands[0]; + Assert.IsType(operand); + var immOperand = (ImmediateOperand)operand; + Assert.Equal(0x42u, immOperand.Value); + Assert.Equal(8, immOperand.Size); + } +} diff --git a/X86DisassemblerTests/InstructionTests/SegmentOverrideTests.cs b/X86DisassemblerTests/InstructionTests/SegmentOverrideTests.cs index 29edb4c..35d7414 100644 --- a/X86DisassemblerTests/InstructionTests/SegmentOverrideTests.cs +++ b/X86DisassemblerTests/InstructionTests/SegmentOverrideTests.cs @@ -245,27 +245,6 @@ public class SegmentOverrideTests Assert.Equal(-4, memoryOperand.Displacement); } - /// - /// Tests that the FS segment override prefix (0x64) is correctly recognized when it's the only byte - /// - [Fact] - public void FsSegmentOverride_Alone_IsRecognized() - { - // Arrange - // Just the FS segment override prefix (0x64) - byte[] codeBuffer = new byte[] { 0x64 }; - var disassembler = new Disassembler(codeBuffer, 0); - - // Act - var instructions = disassembler.Disassemble(); - - // Assert - Assert.Single(instructions); - var instruction = instructions[0]; - Assert.NotNull(instruction); - Assert.Equal(InstructionType.Rep, instruction.Type); - } - /// /// Tests segment override with a complex addressing mode /// diff --git a/X86DisassemblerTests/RawFromFileDisassemblyTests.cs b/X86DisassemblerTests/RawFromFileDisassemblyTests.cs index c8e02e1..91d92f6 100644 --- a/X86DisassemblerTests/RawFromFileDisassemblyTests.cs +++ b/X86DisassemblerTests/RawFromFileDisassemblyTests.cs @@ -12,6 +12,8 @@ public class RawFromFileDisassemblyTests(ITestOutputHelper output) [ClassData(typeof(TestDataProvider))] public void RunTests(string f, int idx, TestFromFileEntry test) { + Printer.WriteLine = output.WriteLine; + // Convert hex string to byte array byte[] code = HexStringToByteArray(test.RawBytes); diff --git a/X86DisassemblerTests/TestData/adc_tests.csv b/X86DisassemblerTests/TestData/adc_tests.csv index 7d98d86..e16e3ea 100644 --- a/X86DisassemblerTests/TestData/adc_tests.csv +++ b/X86DisassemblerTests/TestData/adc_tests.csv @@ -2,57 +2,26 @@ # Format: RawBytes;Instructions RawBytes;Instructions -# ADC r/m8, imm8 (opcode 80 /2) -80D042;[{ "Type": "Adc", "Operands": ["al", "0x42"] }] -80D342;[{ "Type": "Adc", "Operands": ["bl", "0x42"] }] -80D142;[{ "Type": "Adc", "Operands": ["cl", "0x42"] }] -80D242;[{ "Type": "Adc", "Operands": ["dl", "0x42"] }] - -# ADC AL, imm8 (opcode 14) -1442;[{ "Type": "Adc", "Operands": ["al", "0x42"] }] - -# ADC r/m32, imm32 (opcode 81 /2) -81D078563412;[{ "Type": "Adc", "Operands": ["eax", "0x12345678"] }] -81D378563412;[{ "Type": "Adc", "Operands": ["ebx", "0x12345678"] }] -81D178563412;[{ "Type": "Adc", "Operands": ["ecx", "0x12345678"] }] -81D278563412;[{ "Type": "Adc", "Operands": ["edx", "0x12345678"] }] - -# ADC EAX, imm32 (opcode 15) -1578563412;[{ "Type": "Adc", "Operands": ["eax", "0x12345678"] }] - -# ADC r/m32, imm8 (opcode 83 /2) -83D042;[{ "Type": "Adc", "Operands": ["eax", "0x42"] }] -83D342;[{ "Type": "Adc", "Operands": ["ebx", "0x42"] }] -83D142;[{ "Type": "Adc", "Operands": ["ecx", "0x42"] }] -83D242;[{ "Type": "Adc", "Operands": ["edx", "0x42"] }] - -# ADC r/m8, r8 (opcode 10) -10C3;[{ "Type": "Adc", "Operands": ["bl", "al"] }] -10D9;[{ "Type": "Adc", "Operands": ["cl", "bl"] }] -10E2;[{ "Type": "Adc", "Operands": ["dl", "ah"] }] - -# ADC r8, r/m8 (opcode 12) -12C3;[{ "Type": "Adc", "Operands": ["al", "bl"] }] -12D9;[{ "Type": "Adc", "Operands": ["bl", "cl"] }] -12E2;[{ "Type": "Adc", "Operands": ["ah", "dl"] }] - -# ADC r/m32, r32 (opcode 11) +14AA;[{ "Type": "Adc", "Operands": ["al", "0xAA"] }] +80D1AA;[{ "Type": "Adc", "Operands": ["cl", "0xAA"] }] +8010AA;[{ "Type": "Adc", "Operands": ["byte ptr [eax]", "0xAA"] }] +805310AA;[{ "Type": "Adc", "Operands": ["byte ptr [ebx+0x10]", "0xAA"] }] +10D8;[{ "Type": "Adc", "Operands": ["al", "bl"] }] +1018;[{ "Type": "Adc", "Operands": ["byte ptr [eax]", "bl"] }] +1218;[{ "Type": "Adc", "Operands": ["bl", "byte ptr [eax]"] }] +801488AA;[{ "Type": "Adc", "Operands": ["byte ptr [eax+ecx*4]", "0xAA"] }] +6681D1AA00;[{ "Type": "Adc", "Operands": ["cx", "0xAA"] }] +668110AA00;[{ "Type": "Adc", "Operands": ["word ptr [eax]", "0xAA"] }] +66815310AA00;[{ "Type": "Adc", "Operands": ["word ptr [ebx+0x10]", "0xAA"] }] +6611D8;[{ "Type": "Adc", "Operands": ["ax", "bx"] }] +661118;[{ "Type": "Adc", "Operands": ["word ptr [eax]", "bx"] }] +661318;[{ "Type": "Adc", "Operands": ["bx", "word ptr [eax]"] }] +66811488AA00;[{ "Type": "Adc", "Operands": ["word ptr [eax+ecx*4]", "0xAA"] }] +15AAAA0000;[{ "Type": "Adc", "Operands": ["eax", "0xAAAA"] }] +81D1AAAA0000;[{ "Type": "Adc", "Operands": ["ecx", "0xAAAA"] }] +8110AAAA0000;[{ "Type": "Adc", "Operands": ["dword ptr [eax]", "0xAAAA"] }] +815310AAAA0000;[{ "Type": "Adc", "Operands": ["dword ptr [ebx+0x10]", "0xAAAA"] }] 11D8;[{ "Type": "Adc", "Operands": ["eax", "ebx"] }] -11CA;[{ "Type": "Adc", "Operands": ["edx", "ecx"] }] -11E5;[{ "Type": "Adc", "Operands": ["ebp", "esp"] }] -114B10;[{ "Type": "Adc", "Operands": ["dword ptr [ebx+0x10]", "ecx"] }] - -# ADC r32, r/m32 (opcode 13) -13D8;[{ "Type": "Adc", "Operands": ["ebx", "eax"] }] -13CA;[{ "Type": "Adc", "Operands": ["ecx", "edx"] }] -13E5;[{ "Type": "Adc", "Operands": ["esp", "ebp"] }] -134B10;[{ "Type": "Adc", "Operands": ["ecx", "dword ptr [ebx+0x10]"] }] - -# ADC with memory operands -8014251000000042;[{ "Type": "Adc", "Operands": ["byte ptr [0x10]", "0x42"] }] -8114251000000078563412;[{ "Type": "Adc", "Operands": ["dword ptr [0x10]", "0x12345678"] }] -8314251000000042;[{ "Type": "Adc", "Operands": ["dword ptr [0x10]", "0x42"] }] -1004251000000000;[{ "Type": "Adc", "Operands": ["byte ptr [0x10]", "al"] }] -1204251000000000;[{ "Type": "Adc", "Operands": ["al", "byte ptr [0x10]"] }] -1104251000000000;[{ "Type": "Adc", "Operands": ["dword ptr [0x10]", "eax"] }] -1304251000000000;[{ "Type": "Adc", "Operands": ["eax", "dword ptr [0x10]"] }] +1118;[{ "Type": "Adc", "Operands": ["dword ptr [eax]", "ebx"] }] +1318;[{ "Type": "Adc", "Operands": ["ebx", "dword ptr [eax]"] }] +811488AAAA0000;[{ "Type": "Adc", "Operands": ["dword ptr [eax+ecx*4]", "0xAAAA"] }] \ No newline at end of file diff --git a/X86DisassemblerTests/TestData/add_tests.csv b/X86DisassemblerTests/TestData/add_tests.csv index 14e0cbf..0e505b8 100644 --- a/X86DisassemblerTests/TestData/add_tests.csv +++ b/X86DisassemblerTests/TestData/add_tests.csv @@ -1,60 +1,36 @@ -# ADD instruction tests # Format: RawBytes;Instructions RawBytes;Instructions +# 8-bit ADD +04AA;[{ "Type": "Add", "Operands": ["al", "0xAA"] }] +80C1AA;[{ "Type": "Add", "Operands": ["cl", "0xAA"] }] +8000AA;[{ "Type": "Add", "Operands": ["byte ptr [eax]", "0xAA"] }] +805310AA;[{ "Type": "Add", "Operands": ["byte ptr [ebx+0x10]", "0xAA"] }] +00D8;[{ "Type": "Add", "Operands": ["al", "bl"] }] +0018;[{ "Type": "Add", "Operands": ["byte ptr [eax]", "bl"] }] +0218;[{ "Type": "Add", "Operands": ["bl", "byte ptr [eax]"] }] +800488AA;[{ "Type": "Add", "Operands": ["byte ptr [eax+ecx*4]", "0xAA"] }] -# ADD r/m8, imm8 (opcode 80 /0) -80C042;[{ "Type": "Add", "Operands": ["al", "0x42"] }] -80C342;[{ "Type": "Add", "Operands": ["bl", "0x42"] }] -80C142;[{ "Type": "Add", "Operands": ["cl", "0x42"] }] -80C242;[{ "Type": "Add", "Operands": ["dl", "0x42"] }] +# 16-bit ADD (with 66 prefix) +6605AA00;[{ "Type": "Add", "Operands": ["ax", "0xAA"] }] +6681C1AA00;[{ "Type": "Add", "Operands": ["cx", "0xAA"] }] +668100AA00;[{ "Type": "Add", "Operands": ["word ptr [eax]", "0xAA"] }] +66814310AA00;[{ "Type": "Add", "Operands": ["word ptr [ebx+0x10]", "0xAA"] }] +6601D8;[{ "Type": "Add", "Operands": ["ax", "bx"] }] +660118;[{ "Type": "Add", "Operands": ["word ptr [eax]", "bx"] }] +660318;[{ "Type": "Add", "Operands": ["bx", "word ptr [eax]"] }] +66810488AA00;[{ "Type": "Add", "Operands": ["word ptr [eax+ecx*4]", "0xAA"] }] -# ADD AL, imm8 (opcode 04) -0442;[{ "Type": "Add", "Operands": ["al", "0x42"] }] - -# ADD r/m32, imm32 (opcode 81 /0) -81C078563412;[{ "Type": "Add", "Operands": ["eax", "0x12345678"] }] -81C378563412;[{ "Type": "Add", "Operands": ["ebx", "0x12345678"] }] -81C178563412;[{ "Type": "Add", "Operands": ["ecx", "0x12345678"] }] -81C278563412;[{ "Type": "Add", "Operands": ["edx", "0x12345678"] }] - -# ADD EAX, imm32 (opcode 05) -0578563412;[{ "Type": "Add", "Operands": ["eax", "0x12345678"] }] - -# ADD r/m32, imm8 (opcode 83 /0) with sign extension -83C042;[{ "Type": "Add", "Operands": ["eax", "0x42"] }] -83C342;[{ "Type": "Add", "Operands": ["ebx", "0x42"] }] -83C142;[{ "Type": "Add", "Operands": ["ecx", "0x42"] }] -83C242;[{ "Type": "Add", "Operands": ["edx", "0x42"] }] -83C0FF;[{ "Type": "Add", "Operands": ["eax", "0xFFFFFFFF"] }] -83C3FF;[{ "Type": "Add", "Operands": ["ebx", "0xFFFFFFFF"] }] - -# ADD r/m8, r8 (opcode 00) -00C3;[{ "Type": "Add", "Operands": ["bl", "al"] }] -00D9;[{ "Type": "Add", "Operands": ["cl", "bl"] }] -00E2;[{ "Type": "Add", "Operands": ["dl", "ah"] }] - -# ADD r8, r/m8 (opcode 02) -02C3;[{ "Type": "Add", "Operands": ["al", "bl"] }] -02D9;[{ "Type": "Add", "Operands": ["bl", "cl"] }] -02E2;[{ "Type": "Add", "Operands": ["ah", "dl"] }] - -# ADD r/m32, r32 (opcode 01) +# 32-bit ADD +05AA000000;[{ "Type": "Add", "Operands": ["eax", "0xAA"] }] +81C1AA000000;[{ "Type": "Add", "Operands": ["ecx", "0xAA"] }] +8100AA000000;[{ "Type": "Add", "Operands": ["dword ptr [eax]", "0xAA"] }] +814310AA000000;[{ "Type": "Add", "Operands": ["dword ptr [ebx+0x10]", "0xAA"] }] 01D8;[{ "Type": "Add", "Operands": ["eax", "ebx"] }] -01CA;[{ "Type": "Add", "Operands": ["edx", "ecx"] }] -01E5;[{ "Type": "Add", "Operands": ["ebp", "esp"] }] -014B10;[{ "Type": "Add", "Operands": ["dword ptr [ebx+0x10]", "ecx"] }] +0118;[{ "Type": "Add", "Operands": ["dword ptr [eax]", "ebx"] }] +0318;[{ "Type": "Add", "Operands": ["ebx", "dword ptr [eax]"] }] +810488AA000000;[{ "Type": "Add", "Operands": ["dword ptr [eax+ecx*4]", "0xAA"] }] -# ADD r32, r/m32 (opcode 03) -03D8;[{ "Type": "Add", "Operands": ["ebx", "eax"] }] -03CA;[{ "Type": "Add", "Operands": ["ecx", "edx"] }] -03E5;[{ "Type": "Add", "Operands": ["esp", "ebp"] }] -034B10;[{ "Type": "Add", "Operands": ["ecx", "dword ptr [ebx+0x10]"] }] - -# ADD with memory operands -8004251000000042;[{ "Type": "Add", "Operands": ["byte ptr [0x10]", "0x42"] }] -8104251000000078563412;[{ "Type": "Add", "Operands": ["dword ptr [0x10]", "0x12345678"] }] -8304251000000042;[{ "Type": "Add", "Operands": ["dword ptr [0x10]", "0x42"] }] -00042510000000;[{ "Type": "Add", "Operands": ["byte ptr [0x10]", "al"] }] -02042510000000;[{ "Type": "Add", "Operands": ["al", "byte ptr [0x10]"] }] -01042510000000;[{ "Type": "Add", "Operands": ["dword ptr [0x10]", "eax"] }] -03042510000000;[{ "Type": "Add", "Operands": ["eax", "dword ptr [0x10]"] }] +# Mixed addressing modes +00A314285600;[{ "Type": "Add", "Operands": ["byte ptr [ebx+0x562814]", "ah"] }] +6601B310203040;[{ "Type": "Add", "Operands": ["si", "word ptr [ebx+0x40302010]"] }] +030C8D10203040;[{ "Type": "Add", "Operands": ["ecx", "dword ptr [ebp*4+0x40302010]"] }] \ No newline at end of file diff --git a/X86DisassemblerTests/TestData/and_tests.csv b/X86DisassemblerTests/TestData/and_tests.csv index 949721d..f439492 100644 --- a/X86DisassemblerTests/TestData/and_tests.csv +++ b/X86DisassemblerTests/TestData/and_tests.csv @@ -1,58 +1,42 @@ -# AND instruction tests # Format: RawBytes;Instructions RawBytes;Instructions +# 8-bit AND +24AA;[{ "Type": "And", "Operands": ["al", "0xAA"] }] +# Alternate encoding +80E0AA;[{ "Type": "And", "Operands": ["al", "0xAA"] }] +80E1AA;[{ "Type": "And", "Operands": ["cl", "0xAA"] }] +8020AA;[{ "Type": "And", "Operands": ["byte ptr [eax]", "0xAA"] }] +806310AA;[{ "Type": "And", "Operands": ["byte ptr [ebx+0x10]", "0xAA"] }] +20D8;[{ "Type": "And", "Operands": ["al", "bl"] }] +2018;[{ "Type": "And", "Operands": ["byte ptr [eax]", "bl"] }] +2218;[{ "Type": "And", "Operands": ["bl", "byte ptr [eax]"] }] +800488AA;[{ "Type": "And", "Operands": ["byte ptr [eax+ecx*4]", "0xAA"] }] -# AND r/m8, imm8 (opcode 80 /4) -80E042;[{ "Type": "And", "Operands": ["al", "0x42"] }] -80E342;[{ "Type": "And", "Operands": ["bl", "0x42"] }] -80E142;[{ "Type": "And", "Operands": ["cl", "0x42"] }] -80E242;[{ "Type": "And", "Operands": ["dl", "0x42"] }] +# 16-bit AND (with 66 prefix) +6625AA00;[{ "Type": "And", "Operands": ["ax", "0xAA"] }] +# Alternate encoding +6681E0AA00;[{ "Type": "And", "Operands": ["ax", "0xAA"] }] +6681E1AA00;[{ "Type": "And", "Operands": ["cx", "0xAA"] }] +668120AA00;[{ "Type": "And", "Operands": ["word ptr [eax]", "0xAA"] }] +66816310AA00;[{ "Type": "And", "Operands": ["word ptr [ebx+0x10]", "0xAA"] }] +6621D8;[{ "Type": "And", "Operands": ["ax", "bx"] }] +662118;[{ "Type": "And", "Operands": ["word ptr [eax]", "bx"] }] +662318;[{ "Type": "And", "Operands": ["bx", "word ptr [eax]"] }] +66810488AA00;[{ "Type": "And", "Operands": ["word ptr [eax+ecx*4]", "0xAA"] }] -# AND AL, imm8 (opcode 24) -2442;[{ "Type": "And", "Operands": ["al", "0x42"] }] - -# AND r/m32, imm32 (opcode 81 /4) -81E078563412;[{ "Type": "And", "Operands": ["eax", "0x12345678"] }] -81E378563412;[{ "Type": "And", "Operands": ["ebx", "0x12345678"] }] -81E178563412;[{ "Type": "And", "Operands": ["ecx", "0x12345678"] }] -81E278563412;[{ "Type": "And", "Operands": ["edx", "0x12345678"] }] - -# AND EAX, imm32 (opcode 25) -2578563412;[{ "Type": "And", "Operands": ["eax", "0x12345678"] }] - -# AND r/m32, imm8 (opcode 83 /4) -83E042;[{ "Type": "And", "Operands": ["eax", "0x42"] }] -83E342;[{ "Type": "And", "Operands": ["ebx", "0x42"] }] -83E142;[{ "Type": "And", "Operands": ["ecx", "0x42"] }] -83E242;[{ "Type": "And", "Operands": ["edx", "0x42"] }] - -# AND r/m8, r8 (opcode 20) -20C3;[{ "Type": "And", "Operands": ["bl", "al"] }] -20D9;[{ "Type": "And", "Operands": ["cl", "bl"] }] -20E2;[{ "Type": "And", "Operands": ["dl", "ah"] }] - -# AND r8, r/m8 (opcode 22) -22C3;[{ "Type": "And", "Operands": ["al", "bl"] }] -22D9;[{ "Type": "And", "Operands": ["bl", "cl"] }] -22E2;[{ "Type": "And", "Operands": ["ah", "dl"] }] - -# AND r/m32, r32 (opcode 21) +# 32-bit AND +25AA000000;[{ "Type": "And", "Operands": ["eax", "0xAA"] }] +# Alternate encoding +81E0AA000000;[{ "Type": "And", "Operands": ["eax", "0xAA"] }] +81E1AA000000;[{ "Type": "And", "Operands": ["ecx", "0xAA"] }] +8120AA000000;[{ "Type": "And", "Operands": ["dword ptr [eax]", "0xAA"] }] +816310AA000000;[{ "Type": "And", "Operands": ["dword ptr [ebx+0x10]", "0xAA"] }] 21D8;[{ "Type": "And", "Operands": ["eax", "ebx"] }] -21CA;[{ "Type": "And", "Operands": ["edx", "ecx"] }] -21E5;[{ "Type": "And", "Operands": ["ebp", "esp"] }] -214B10;[{ "Type": "And", "Operands": ["dword ptr [ebx+0x10]", "ecx"] }] +2118;[{ "Type": "And", "Operands": ["dword ptr [eax]", "ebx"] }] +2318;[{ "Type": "And", "Operands": ["ebx", "dword ptr [eax]"] }] +810488AA000000;[{ "Type": "And", "Operands": ["dword ptr [eax+ecx*4]", "0xAA"] }] -# AND r32, r/m32 (opcode 23) -23D8;[{ "Type": "And", "Operands": ["ebx", "eax"] }] -23CA;[{ "Type": "And", "Operands": ["ecx", "edx"] }] -23E5;[{ "Type": "And", "Operands": ["esp", "ebp"] }] -234B10;[{ "Type": "And", "Operands": ["ecx", "dword ptr [ebx+0x10]"] }] - -# AND with memory operands -8024251000000042;[{ "Type": "And", "Operands": ["byte ptr [0x10]", "0x42"] }] -8124251000000078563412;[{ "Type": "And", "Operands": ["dword ptr [0x10]", "0x12345678"] }] -8324251000000042;[{ "Type": "And", "Operands": ["dword ptr [0x10]", "0x42"] }] -20042510000000;[{ "Type": "And", "Operands": ["byte ptr [0x10]", "al"] }] -22042510000000;[{ "Type": "And", "Operands": ["al", "byte ptr [0x10]"] }] -21042510000000;[{ "Type": "And", "Operands": ["dword ptr [0x10]", "eax"] }] -23042510000000;[{ "Type": "And", "Operands": ["eax", "dword ptr [0x10]"] }] +# Complex addressing modes +20A314285600;[{ "Type": "And", "Operands": ["byte ptr [ebx+0x562814]", "ah"] }] +6621B310203040;[{ "Type": "And", "Operands": ["word ptr [ebx+0x40302010]", "si"] }] +230C8D10203040;[{ "Type": "And", "Operands": ["ecx", "dword ptr [ebp*4+0x40302010]"] }] \ No newline at end of file diff --git a/X86DisassemblerTests/TestData/bit_tests.csv b/X86DisassemblerTests/TestData/bit_tests.csv index a6d7fc2..84319d1 100644 --- a/X86DisassemblerTests/TestData/bit_tests.csv +++ b/X86DisassemblerTests/TestData/bit_tests.csv @@ -2,20 +2,40 @@ # Format: RawBytes;Instructions RawBytes;Instructions +# BT - Bit Test (Immediate) +0FBA2005;[{ "Type": "Bt", "Operands": ["dword ptr [eax]", "0x05"] }] +0FBA650005;[{ "Type": "Bt", "Operands": ["dword ptr [ebp+0x00]", "0x05"] }] +0FBA2305;[{ "Type": "Bt", "Operands": ["dword ptr [ebx]", "0x05"] }] +0FBA2105;[{ "Type": "Bt", "Operands": ["dword ptr [ecx]", "0x05"] }] +0FBA2205;[{ "Type": "Bt", "Operands": ["dword ptr [edx]", "0x05"] }] + +# BTS - Bit Test and Set (Immediate) +0FBAA805;[{ "Type": "Bts", "Operands": ["dword ptr [eax]", "0x05"] }] +0FBA6D0005;[{ "Type": "Bts", "Operands": ["dword ptr [ebp+0x00]", "0x05"] }] +0FBAAB05;[{ "Type": "Bts", "Operands": ["dword ptr [ebx]", "0x05"] }] +0FBAA905;[{ "Type": "Bts", "Operands": ["dword ptr [ecx]", "0x05"] }] +0FBAAA05;[{ "Type": "Bts", "Operands": ["dword ptr [edx]", "0x05"] }] + +# BTR - Bit Test and Reset (Immediate) +0FBAB005;[{ "Type": "Btr", "Operands": ["dword ptr [eax]", "0x05"] }] +0FBA750005;[{ "Type": "Btr", "Operands": ["dword ptr [ebp+0x00]", "0x05"] }] +0FBAB305;[{ "Type": "Btr", "Operands": ["dword ptr [ebx]", "0x05"] }] +0FBAB105;[{ "Type": "Btr", "Operands": ["dword ptr [ecx]", "0x05"] }] +0FBAB205;[{ "Type": "Btr", "Operands": ["dword ptr [edx]", "0x05"] }] + +# BTC - Bit Test and Complement (Immediate) +0FBAB805;[{ "Type": "Btc", "Operands": ["dword ptr [eax]", "0x05"] }] +0FBA7D0005;[{ "Type": "Btc", "Operands": ["dword ptr [ebp+0x00]", "0x05"] }] +0FBABB05;[{ "Type": "Btc", "Operands": ["dword ptr [ebx]", "0x05"] }] +0FBAB905;[{ "Type": "Btc", "Operands": ["dword ptr [ecx]", "0x05"] }] +0FBABA05;[{ "Type": "Btc", "Operands": ["dword ptr [edx]", "0x05"] }] + # BT - Bit Test 0FA3C1;[{ "Type": "Bt", "Operands": ["ecx", "eax"] }] 0FA3D9;[{ "Type": "Bt", "Operands": ["ecx", "ebx"] }] 0FA3CA;[{ "Type": "Bt", "Operands": ["edx", "ecx"] }] 0FA3E2;[{ "Type": "Bt", "Operands": ["edx", "esp"] }] 0FA3F6;[{ "Type": "Bt", "Operands": ["esi", "esi"] }] -0FA30425;[{ "Type": "Bt", "Operands": ["dword ptr [eax]", "eax"] }] -0FA30C25;[{ "Type": "Bt", "Operands": ["dword ptr [eax]", "ecx"] }] -0FA31425;[{ "Type": "Bt", "Operands": ["dword ptr [eax]", "edx"] }] -0FBA2005;[{ "Type": "Bt", "Operands": ["dword ptr [eax]", "0x05"] }] -0FBA2505;[{ "Type": "Bt", "Operands": ["dword ptr [ebp]", "0x05"] }] -0FBA2305;[{ "Type": "Bt", "Operands": ["dword ptr [ebx]", "0x05"] }] -0FBA2105;[{ "Type": "Bt", "Operands": ["dword ptr [ecx]", "0x05"] }] -0FBA2205;[{ "Type": "Bt", "Operands": ["dword ptr [edx]", "0x05"] }] # BTS - Bit Test and Set 0FABC1;[{ "Type": "Bts", "Operands": ["ecx", "eax"] }] @@ -23,14 +43,6 @@ RawBytes;Instructions 0FABCA;[{ "Type": "Bts", "Operands": ["edx", "ecx"] }] 0FABE2;[{ "Type": "Bts", "Operands": ["edx", "esp"] }] 0FABF6;[{ "Type": "Bts", "Operands": ["esi", "esi"] }] -0FAB0425;[{ "Type": "Bts", "Operands": ["dword ptr [eax]", "eax"] }] -0FAB0C25;[{ "Type": "Bts", "Operands": ["dword ptr [eax]", "ecx"] }] -0FAB1425;[{ "Type": "Bts", "Operands": ["dword ptr [eax]", "edx"] }] -0FBA2805;[{ "Type": "Bts", "Operands": ["dword ptr [eax]", "0x05"] }] -0FBA2D05;[{ "Type": "Bts", "Operands": ["dword ptr [ebp]", "0x05"] }] -0FBA2B05;[{ "Type": "Bts", "Operands": ["dword ptr [ebx]", "0x05"] }] -0FBA2905;[{ "Type": "Bts", "Operands": ["dword ptr [ecx]", "0x05"] }] -0FBA2A05;[{ "Type": "Bts", "Operands": ["dword ptr [edx]", "0x05"] }] # BTR - Bit Test and Reset 0FB3C1;[{ "Type": "Btr", "Operands": ["ecx", "eax"] }] @@ -38,29 +50,6 @@ RawBytes;Instructions 0FB3CA;[{ "Type": "Btr", "Operands": ["edx", "ecx"] }] 0FB3E2;[{ "Type": "Btr", "Operands": ["edx", "esp"] }] 0FB3F6;[{ "Type": "Btr", "Operands": ["esi", "esi"] }] -0FB30425;[{ "Type": "Btr", "Operands": ["dword ptr [eax]", "eax"] }] -0FB30C25;[{ "Type": "Btr", "Operands": ["dword ptr [eax]", "ecx"] }] -0FB31425;[{ "Type": "Btr", "Operands": ["dword ptr [eax]", "edx"] }] -0FBA3005;[{ "Type": "Btr", "Operands": ["dword ptr [eax]", "0x05"] }] -0FBA3505;[{ "Type": "Btr", "Operands": ["dword ptr [ebp]", "0x05"] }] -0FBA3305;[{ "Type": "Btr", "Operands": ["dword ptr [ebx]", "0x05"] }] -0FBA3105;[{ "Type": "Btr", "Operands": ["dword ptr [ecx]", "0x05"] }] -0FBA3205;[{ "Type": "Btr", "Operands": ["dword ptr [edx]", "0x05"] }] - -# BTC - Bit Test and Complement -0FBBC1;[{ "Type": "Btc", "Operands": ["ecx", "eax"] }] -0FBBD9;[{ "Type": "Btc", "Operands": ["ecx", "ebx"] }] -0FBBCA;[{ "Type": "Btc", "Operands": ["edx", "ecx"] }] -0FBBE2;[{ "Type": "Btc", "Operands": ["edx", "esp"] }] -0FBBF6;[{ "Type": "Btc", "Operands": ["esi", "esi"] }] -0FBB0425;[{ "Type": "Btc", "Operands": ["dword ptr [eax]", "eax"] }] -0FBB0C25;[{ "Type": "Btc", "Operands": ["dword ptr [eax]", "ecx"] }] -0FBB1425;[{ "Type": "Btc", "Operands": ["dword ptr [eax]", "edx"] }] -0FBA3805;[{ "Type": "Btc", "Operands": ["dword ptr [eax]", "0x05"] }] -0FBA3D05;[{ "Type": "Btc", "Operands": ["dword ptr [ebp]", "0x05"] }] -0FBA3B05;[{ "Type": "Btc", "Operands": ["dword ptr [ebx]", "0x05"] }] -0FBA3905;[{ "Type": "Btc", "Operands": ["dword ptr [ecx]", "0x05"] }] -0FBA3A05;[{ "Type": "Btc", "Operands": ["dword ptr [edx]", "0x05"] }] # BSF - Bit Scan Forward 0FBCC1;[{ "Type": "Bsf", "Operands": ["eax", "ecx"] }] @@ -68,11 +57,20 @@ RawBytes;Instructions 0FBCCA;[{ "Type": "Bsf", "Operands": ["ecx", "edx"] }] 0FBCE2;[{ "Type": "Bsf", "Operands": ["esp", "edx"] }] 0FBCF6;[{ "Type": "Bsf", "Operands": ["esi", "esi"] }] -0FBC0425;[{ "Type": "Bsf", "Operands": ["eax", "dword ptr [eax]"] }] -0FBC0C25;[{ "Type": "Bsf", "Operands": ["ecx", "dword ptr [eax]"] }] -0FBC1425;[{ "Type": "Bsf", "Operands": ["edx", "dword ptr [eax]"] }] -0FBC1C25;[{ "Type": "Bsf", "Operands": ["ebx", "dword ptr [eax]"] }] -0FBC2425;[{ "Type": "Bsf", "Operands": ["esp", "dword ptr [eax]"] }] + +# BTC - Bit Test and Complement +0FBBC1;[{ "Type": "Btc", "Operands": ["ecx", "eax"] }] +0FBBD9;[{ "Type": "Btc", "Operands": ["ecx", "ebx"] }] +0FBBCA;[{ "Type": "Btc", "Operands": ["edx", "ecx"] }] +0FBBE2;[{ "Type": "Btc", "Operands": ["edx", "esp"] }] +0FBBF6;[{ "Type": "Btc", "Operands": ["esi", "esi"] }] + +# BSF - Bit Scan Forward +0FBC00;[{ "Type": "Bsf", "Operands": ["eax", "dword ptr [eax]"] }] +0FBC08;[{ "Type": "Bsf", "Operands": ["ecx", "dword ptr [eax]"] }] +0FBC10;[{ "Type": "Bsf", "Operands": ["edx", "dword ptr [eax]"] }] +0FBC18;[{ "Type": "Bsf", "Operands": ["ebx", "dword ptr [eax]"] }] +0FBC20;[{ "Type": "Bsf", "Operands": ["esp", "dword ptr [eax]"] }] # BSR - Bit Scan Reverse 0FBDC1;[{ "Type": "Bsr", "Operands": ["eax", "ecx"] }] @@ -80,8 +78,8 @@ RawBytes;Instructions 0FBDCA;[{ "Type": "Bsr", "Operands": ["ecx", "edx"] }] 0FBDE2;[{ "Type": "Bsr", "Operands": ["esp", "edx"] }] 0FBDF6;[{ "Type": "Bsr", "Operands": ["esi", "esi"] }] -0FBD0425;[{ "Type": "Bsr", "Operands": ["eax", "dword ptr [eax]"] }] -0FBD0C25;[{ "Type": "Bsr", "Operands": ["ecx", "dword ptr [eax]"] }] -0FBD1425;[{ "Type": "Bsr", "Operands": ["edx", "dword ptr [eax]"] }] -0FBD1C25;[{ "Type": "Bsr", "Operands": ["ebx", "dword ptr [eax]"] }] -0FBD2425;[{ "Type": "Bsr", "Operands": ["esp", "dword ptr [eax]"] }] +0FBD00;[{ "Type": "Bsr", "Operands": ["eax", "dword ptr [eax]"] }] +0FBD08;[{ "Type": "Bsr", "Operands": ["ecx", "dword ptr [eax]"] }] +0FBD10;[{ "Type": "Bsr", "Operands": ["edx", "dword ptr [eax]"] }] +0FBD18;[{ "Type": "Bsr", "Operands": ["ebx", "dword ptr [eax]"] }] +0FBD20;[{ "Type": "Bsr", "Operands": ["esp", "dword ptr [eax]"] }] diff --git a/X86DisassemblerTests/TestData/div_tests.csv b/X86DisassemblerTests/TestData/div_tests.csv index 3b46f5a..a27b0d2 100644 --- a/X86DisassemblerTests/TestData/div_tests.csv +++ b/X86DisassemblerTests/TestData/div_tests.csv @@ -22,46 +22,23 @@ F7F5;[{ "Type": "Div", "Operands": ["ebp"] }] F7F6;[{ "Type": "Div", "Operands": ["esi"] }] F7F7;[{ "Type": "Div", "Operands": ["edi"] }] -# DIV with memory operands # Basic memory addressing -# SPECIAL CASE: When using SIB byte with Base=101 (EBP) and Mod=00, it requires a 32-bit displacement -# The correct encoding for "DIV byte ptr [eax]" would be F630 (with Mod=00, R/M=0 for EAX) -# F63425;[{ "Type": "Div", "Operands": ["byte ptr [eax]"] }] F630;[{ "Type": "Div", "Operands": ["byte ptr [eax]"] }] -# For "DIV byte ptr [ebp]", we need to use Mod=01 with a zero displacement since [ebp] can't be encoded with Mod=00 -# F63C25;[{ "Type": "Div", "Operands": ["byte ptr [ebp]"] }] -F66500;[{ "Type": "Div", "Operands": ["byte ptr [ebp]"] }] - -# The correct encoding for "DIV byte ptr [eax]" would be F630 (with Mod=00, R/M=0 for EAX) -# F63825;[{ "Type": "Div", "Operands": ["byte ptr [eax]"] }] F630;[{ "Type": "Div", "Operands": ["byte ptr [eax]"] }] -# The correct encoding for "DIV dword ptr [eax]" would be F730 (with Mod=00, R/M=0 for EAX) -# F73425;[{ "Type": "Div", "Operands": ["dword ptr [eax]"] }] F730;[{ "Type": "Div", "Operands": ["dword ptr [eax]"] }] -# For "DIV dword ptr [ebp]", we need to use Mod=01 with a zero displacement since [ebp] can't be encoded with Mod=00 -# F73C25;[{ "Type": "Div", "Operands": ["dword ptr [ebp]"] }] -F76500;[{ "Type": "Div", "Operands": ["dword ptr [ebp]"] }] +F77500;[{ "Type": "Div", "Operands": ["dword ptr [ebp+0x00]"] }] -# The correct encoding for "DIV dword ptr [eax]" would be F730 (with Mod=00, R/M=0 for EAX) -# F73825;[{ "Type": "Div", "Operands": ["dword ptr [eax]"] }] F730;[{ "Type": "Div", "Operands": ["dword ptr [eax]"] }] -# With displacement -# The correct encoding for "DIV dword ptr [eax+0x10]" would be F74010 (with Mod=01, R/M=0 for EAX, disp8=0x10) -# F7742510000000;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x10]"] }] -F74010;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x10]"] }] +F77010;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x10]"] }] -# The correct encoding for "DIV dword ptr [eax+0x20]" would be F74020 (with Mod=01, R/M=0 for EAX, disp8=0x20) -# F7742520000000;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x20]"] }] -F74020;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x20]"] }] +F77020;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x20]"] }] -# The correct encoding for "DIV dword ptr [eax+0x30]" would be F74030 (with Mod=01, R/M=0 for EAX, disp8=0x30) -# F7742530000000;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x30]"] }] -F74030;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x30]"] }] +F77030;[{ "Type": "Div", "Operands": ["dword ptr [eax+0x30]"] }] # With SIB addressing F7341C;[{ "Type": "Div", "Operands": ["dword ptr [esp+ebx*1]"] }] @@ -70,10 +47,9 @@ F7349C;[{ "Type": "Div", "Operands": ["dword ptr [esp+ebx*4]"] }] F734DC;[{ "Type": "Div", "Operands": ["dword ptr [esp+ebx*8]"] }] # With segment override prefixes -# not recognized by ghidra or online disasms -# 26F73425;[{ "Type": "Div", "Operands": ["dword ptr es:[eax]"] }] -# 2EF73425;[{ "Type": "Div", "Operands": ["dword ptr cs:[eax]"] }] -# 36F73425;[{ "Type": "Div", "Operands": ["dword ptr ss:[eax]"] }] -# 3EF73425;[{ "Type": "Div", "Operands": ["dword ptr ds:[eax]"] }] -# 64F73425;[{ "Type": "Div", "Operands": ["dword ptr fs:[eax]"] }] -# 65F73425;[{ "Type": "Div", "Operands": ["dword ptr gs:[eax]"] }] +26F730;[{ "Type": "Div", "Operands": ["dword ptr es:[eax]"] }] +2EF730;[{ "Type": "Div", "Operands": ["dword ptr cs:[eax]"] }] +36F730;[{ "Type": "Div", "Operands": ["dword ptr ss:[eax]"] }] +3EF730;[{ "Type": "Div", "Operands": ["dword ptr ds:[eax]"] }] +64F730;[{ "Type": "Div", "Operands": ["dword ptr fs:[eax]"] }] +65F730;[{ "Type": "Div", "Operands": ["dword ptr gs:[eax]"] }] diff --git a/X86DisassemblerTests/TestData/poprm_tests.csv b/X86DisassemblerTests/TestData/poprm_tests.csv index e3a1cb8..0cd741f 100644 --- a/X86DisassemblerTests/TestData/poprm_tests.csv +++ b/X86DisassemblerTests/TestData/poprm_tests.csv @@ -7,8 +7,8 @@ RawBytes;Instructions 8F01;[{ "Type": "Pop", "Operands": ["dword ptr [ecx]"] }] 8F02;[{ "Type": "Pop", "Operands": ["dword ptr [edx]"] }] 8F03;[{ "Type": "Pop", "Operands": ["dword ptr [ebx]"] }] -8F04;[{ "Type": "Pop", "Operands": ["dword ptr [esp]"] }] -8F05;[{ "Type": "Pop", "Operands": ["dword ptr [ebp]"] }] +8F0424;[{ "Type": "Pop", "Operands": ["dword ptr [esp]"] }] +88F4500;[{ "Type": "Pop", "Operands": ["dword ptr [ebp]"] }] 8F06;[{ "Type": "Pop", "Operands": ["dword ptr [esi]"] }] 8F07;[{ "Type": "Pop", "Operands": ["dword ptr [edi]"] }] @@ -17,7 +17,7 @@ RawBytes;Instructions 8F4110;[{ "Type": "Pop", "Operands": ["dword ptr [ecx+0x10]"] }] 8F4210;[{ "Type": "Pop", "Operands": ["dword ptr [edx+0x10]"] }] 8F4310;[{ "Type": "Pop", "Operands": ["dword ptr [ebx+0x10]"] }] -8F4410;[{ "Type": "Pop", "Operands": ["dword ptr [esp+0x10]"] }] +8F442410;[{ "Type": "Pop", "Operands": ["dword ptr [esp+0x10]"] }] 8F4510;[{ "Type": "Pop", "Operands": ["dword ptr [ebp+0x10]"] }] 8F4610;[{ "Type": "Pop", "Operands": ["dword ptr [esi+0x10]"] }] 8F4710;[{ "Type": "Pop", "Operands": ["dword ptr [edi+0x10]"] }] diff --git a/X86DisassemblerTests/TestData/pushimm_tests.csv b/X86DisassemblerTests/TestData/pushimm_tests.csv index 33fa8b9..020ac89 100644 --- a/X86DisassemblerTests/TestData/pushimm_tests.csv +++ b/X86DisassemblerTests/TestData/pushimm_tests.csv @@ -11,9 +11,9 @@ RawBytes;Instructions 6AFF;[{ "Type": "Push", "Operands": ["0xFF"] }] # PUSH imm32 with various values -6800000000;[{ "Type": "Push", "Operands": ["0x00000000"] }] +6800000000;[{ "Type": "Push", "Operands": ["0x00"] }] 68FFFFFFFF;[{ "Type": "Push", "Operands": ["0xFFFFFFFF"] }] -6801000000;[{ "Type": "Push", "Operands": ["0x00000001"] }] +6801000000;[{ "Type": "Push", "Operands": ["0x01"] }] # PUSH imm16 with operand size prefix 66687856;[{ "Type": "Push", "Operands": ["0x5678"] }] diff --git a/X86DisassemblerTests/TestData/pushrm_tests.csv b/X86DisassemblerTests/TestData/pushrm_tests.csv index 2b87c68..ad350aa 100644 --- a/X86DisassemblerTests/TestData/pushrm_tests.csv +++ b/X86DisassemblerTests/TestData/pushrm_tests.csv @@ -7,8 +7,8 @@ FF30;[{ "Type": "Push", "Operands": ["dword ptr [eax]"] }] FF31;[{ "Type": "Push", "Operands": ["dword ptr [ecx]"] }] FF32;[{ "Type": "Push", "Operands": ["dword ptr [edx]"] }] FF33;[{ "Type": "Push", "Operands": ["dword ptr [ebx]"] }] -FF34;[{ "Type": "Push", "Operands": ["dword ptr [esp]"] }] -FF35;[{ "Type": "Push", "Operands": ["dword ptr [ebp]"] }] +FF3424;[{ "Type": "Push", "Operands": ["dword ptr [esp]"] }] +FF7500;[{ "Type": "Push", "Operands": ["dword ptr [ebp]"] }] FF36;[{ "Type": "Push", "Operands": ["dword ptr [esi]"] }] FF37;[{ "Type": "Push", "Operands": ["dword ptr [edi]"] }] @@ -17,16 +17,16 @@ FF7010;[{ "Type": "Push", "Operands": ["dword ptr [eax+0x10]"] }] FF7110;[{ "Type": "Push", "Operands": ["dword ptr [ecx+0x10]"] }] FF7210;[{ "Type": "Push", "Operands": ["dword ptr [edx+0x10]"] }] FF7310;[{ "Type": "Push", "Operands": ["dword ptr [ebx+0x10]"] }] -FF7410;[{ "Type": "Push", "Operands": ["dword ptr [esp+0x10]"] }] +FF742410;[{ "Type": "Push", "Operands": ["dword ptr [esp+0x10]"] }] FF7510;[{ "Type": "Push", "Operands": ["dword ptr [ebp+0x10]"] }] FF7610;[{ "Type": "Push", "Operands": ["dword ptr [esi+0x10]"] }] FF7710;[{ "Type": "Push", "Operands": ["dword ptr [edi+0x10]"] }] # PUSH r/m32 (opcode FF /6) with SIB byte -FF34C5;[{ "Type": "Push", "Operands": ["dword ptr [ebp+eax*8]"] }] -FF34CD;[{ "Type": "Push", "Operands": ["dword ptr [ebp+ecx*8]"] }] -FF34D5;[{ "Type": "Push", "Operands": ["dword ptr [ebp+edx*8]"] }] -FF34DD;[{ "Type": "Push", "Operands": ["dword ptr [ebp+ebx*8]"] }] +FF74C500;[{ "Type": "Push", "Operands": ["dword ptr [ebp+eax*8]"] }] +FF74CD00;[{ "Type": "Push", "Operands": ["dword ptr [ebp+ecx*8]"] }] +FF74D500;[{ "Type": "Push", "Operands": ["dword ptr [ebp+edx*8]"] }] +FF74DD00;[{ "Type": "Push", "Operands": ["dword ptr [ebp+ebx*8]"] }] # PUSH r/m32 (opcode FF /6) with direct memory operand FF3578563412;[{ "Type": "Push", "Operands": ["dword ptr [0x12345678]"] }] diff --git a/X86DisassemblerTests/TestData/sub_tests.csv b/X86DisassemblerTests/TestData/sub_tests.csv index 9a85631..345731a 100644 --- a/X86DisassemblerTests/TestData/sub_tests.csv +++ b/X86DisassemblerTests/TestData/sub_tests.csv @@ -39,19 +39,19 @@ RawBytes;Instructions # 16-bit register operations with operand size prefix (0x66) # SUB r/m16, r16 (opcode 29 with 0x66 prefix) -6629D8;[{ "Type": "Sub", "Operands": ["eax", "ebx"] }] +6629D8;[{ "Type": "Sub", "Operands": ["ax", "bx"] }] # SUB r16, r/m16 (opcode 2B with 0x66 prefix) -662BD8;[{ "Type": "Sub", "Operands": ["ebx", "eax"] }] +662BD8;[{ "Type": "Sub", "Operands": ["bx", "ax"] }] # SUB AX, imm16 (opcode 2D with 0x66 prefix) -662D3412;[{ "Type": "Sub", "Operands": ["eax", "0x1234"] }] +662D3412;[{ "Type": "Sub", "Operands": ["ax", "0x1234"] }] # SUB r/m16, imm8 (opcode 83 /5 with 0x66 prefix and sign extension) -6683EB42;[{ "Type": "Sub", "Operands": ["ebx", "0x42"] }] +6683EB42;[{ "Type": "Sub", "Operands": ["bx", "0x42"] }] # SUB r/m16, imm16 (opcode 81 /5 with 0x66 prefix) -6681EB3412;[{ "Type": "Sub", "Operands": ["ebx", "0x1234"] }] +6681EB3412;[{ "Type": "Sub", "Operands": ["bx", "0x1234"] }] # Additional test cases for more complex addressing modes @@ -101,7 +101,7 @@ RawBytes;Instructions 81EF78563412;[{ "Type": "Sub", "Operands": ["edi", "0x12345678"] }] # SUB r/m32, imm32 (opcode 81 /5) with memory operands -818D10000000FFFFFFFF;[{ "Type": "Sub", "Operands": ["dword ptr [ebp+0x10]", "0xFFFFFFFF"] }] +81AD10000000FFFFFFFF;[{ "Type": "Sub", "Operands": ["dword ptr [ebp+0x10]", "0xFFFFFFFF"] }] # Additional tests for SubImmFromRm32SignExtendedHandler # SUB r/m32, imm8 (opcode 83 /5) with sign extension @@ -114,4 +114,4 @@ RawBytes;Instructions 83EFFF;[{ "Type": "Sub", "Operands": ["edi", "0xFFFFFFFF"] }] # SUB r/m32, imm8 (opcode 83 /5) with memory operands and sign extension -838D1000000080;[{ "Type": "Sub", "Operands": ["dword ptr [ebp+0x10]", "0xFFFFFF80"] }] +83AD1000000080;[{ "Type": "Sub", "Operands": ["dword ptr [ebp+0x10]", "0xFFFFFF80"] }] diff --git a/X86DisassemblerTests/TestData/test_tests.csv b/X86DisassemblerTests/TestData/test_tests.csv index d51f957..0ad5711 100644 --- a/X86DisassemblerTests/TestData/test_tests.csv +++ b/X86DisassemblerTests/TestData/test_tests.csv @@ -19,6 +19,6 @@ F7C378563412;[{ "Type": "Test", "Operands": ["ebx", "0x12345678"] }] 85D9;[{ "Type": "Test", "Operands": ["ecx", "ebx"] }] # TEST with memory operands -F6042542;[{ "Type": "Test", "Operands": ["byte ptr [eax]", "0x42"] }] -F7042578563412;[{ "Type": "Test", "Operands": ["dword ptr [eax]", "0x12345678"] }] -85042510;[{ "Type": "Test", "Operands": ["dword ptr [eax+0x10]", "eax"] }] +F60042;[{ "Type": "Test", "Operands": ["byte ptr [eax]", "0x42"] }] +F70078563412;[{ "Type": "Test", "Operands": ["dword ptr [eax]", "0x12345678"] }] +854510;[{ "Type": "Test", "Operands": ["dword ptr [eax+0x10]", "eax"] }]