using System.Diagnostics; using System.Diagnostics.Contracts; namespace X86Disassembler.X86; using Handlers; using X86Disassembler.X86.Operands; /// /// Decodes x86 instructions from a byte buffer /// public class InstructionDecoder { // The buffer containing the code to decode private readonly byte[] _codeBuffer; // The length of the buffer private readonly int _length; // The current position in the buffer private int _position; // The instruction handler factory private readonly InstructionHandlerFactory _handlerFactory; // Specialized decoders private readonly PrefixDecoder _prefixDecoder; private readonly ModRMDecoder _modRMDecoder; /// /// Initializes a new instance of the InstructionDecoder class /// /// The buffer containing the code to decode /// The length of the buffer public InstructionDecoder(byte[] codeBuffer, int length) { _codeBuffer = codeBuffer; _length = length; _position = 0; // Create specialized decoders _prefixDecoder = new PrefixDecoder(); _modRMDecoder = new ModRMDecoder(this); // Create the instruction handler factory _handlerFactory = new InstructionHandlerFactory(_codeBuffer, this, _length); } /// /// Decodes an instruction at the current position /// /// The decoded instruction, or null if the decoding failed public Instruction? DecodeInstruction() { if (!CanReadByte()) { return null; } // Reset prefix flags _prefixDecoder.Reset(); // Save the start position of the instruction int startPosition = _position; // Create a new instruction Instruction instruction = new Instruction { Address = (uint) startPosition, }; // Handle prefixes while (CanReadByte()) { byte prefix = _codeBuffer[_position]; if (_prefixDecoder.DecodePrefix(prefix)) { _position++; } else { break; } } // If only prefixes were found, return a prefix-only instruction if (_position > startPosition && !CanReadByte()) { // Set the instruction type to Unknown 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}"); bool handlerSuccess = false; // Try to decode with a handler first if (handler != null) { // Store the current segment override state bool hasSegmentOverride = _prefixDecoder.HasSegmentOverridePrefix(); string segmentOverride = _prefixDecoder.GetSegmentOverride(); // Decode the instruction handlerSuccess = handler.Decode(opcode, instruction); // Apply segment override prefix to the structured operands if needed if (handlerSuccess && hasSegmentOverride) { // Apply segment override to memory operands foreach (var operand in instruction.StructuredOperands) { if (operand is Operands.MemoryOperand memoryOperand) { memoryOperand.SegmentOverride = segmentOverride; } } } } else { instruction.Type = InstructionType.Unknown; instruction.StructuredOperands = []; handlerSuccess = true; } // If no handler is found or decoding fails, create a default instruction if (!handlerSuccess) { instruction.Type = InstructionType.Unknown; instruction.StructuredOperands = []; } // Apply REP/REPNE prefix to the instruction type if needed if (_prefixDecoder.HasRepPrefix()) { // For now, we'll keep the original instruction type // In a more complete implementation, we could map instruction types with REP prefix // to specific REP-prefixed instruction types if needed } return instruction; } /// /// Gets the current position in the buffer /// /// The current position [Pure] public int GetPosition() { return _position; } /// /// Sets the current position in the buffer /// /// The new position public void SetPosition(int position) { _position = position; } /// /// Checks if the operand size prefix is present /// /// True if the operand size prefix is present public bool HasOperandSizePrefix() { return _prefixDecoder.HasOperandSizePrefix(); } /// /// Checks if the address size prefix is present /// /// True if the address size prefix is present public bool HasAddressSizePrefix() { return _prefixDecoder.HasAddressSizePrefix(); } /// /// Checks if a segment override prefix is present /// /// True if a segment override prefix is present public bool HasSegmentOverridePrefix() { return _prefixDecoder.HasSegmentOverridePrefix(); } /// /// Gets the segment override prefix /// /// The segment override prefix, or an empty string if none is present public string GetSegmentOverride() { return _prefixDecoder.GetSegmentOverride(); } /// /// Checks if the LOCK prefix is present /// /// True if the LOCK prefix is present public bool HasLockPrefix() { return _prefixDecoder.HasLockPrefix(); } /// /// Checks if the REP/REPNE prefix is present /// /// True if the REP/REPNE prefix is present public bool HasRepPrefix() { return _prefixDecoder.HasRepPrefix(); } /// /// Checks if the instruction has an operand size override prefix (0x66) /// /// True if the instruction has an operand size override prefix public bool HasOperandSizeOverridePrefix() { return _prefixDecoder.HasOperandSizePrefix(); } /// /// Checks if a single byte can be read from the current position /// /// True if there is at least one byte available to read public bool CanReadByte() { return _position < _length; } /// /// Checks if a 16-bit unsigned short (2 bytes) can be read from the current position /// /// True if there are at least two bytes available to read public bool CanReadUShort() { return _position + 1 < _length; } /// /// Checks if a 32-bit unsigned integer (4 bytes) can be read from the current position /// /// True if there are at least four bytes available to read public bool CanReadUInt() { return _position + 3 < _length; } /// /// Peaks a byte from the buffer without adjusting position /// /// The byte peaked public byte PeakByte() { if (_position >= _length) { return 0; } return _codeBuffer[_position]; } /// /// Reads a byte from the buffer and advances the position /// /// The byte read public byte ReadByte() { if (_position >= _length) { return 0; } return _codeBuffer[_position++]; } /// /// Reads a 16-bit value from the buffer and advances the position /// /// The 16-bit value read public ushort ReadUInt16() { if (_position + 1 >= _length) { return 0; } ushort value = (ushort) (_codeBuffer[_position] | (_codeBuffer[_position + 1] << 8)); _position += 2; return value; } /// /// Reads a 32-bit value from the buffer and advances the position /// /// The 32-bit value read public uint ReadUInt32() { if (_position + 3 >= _length) { return 0; } uint value = (uint) (_codeBuffer[_position] | (_codeBuffer[_position + 1] << 8) | (_codeBuffer[_position + 2] << 16) | (_codeBuffer[_position + 3] << 24)); _position += 4; return value; } public bool CanRead(int expectedBytes) { return _position + expectedBytes - 1 < _length; } }