diff --git a/X86Disassembler/X86/Handlers/Cmp/CmpRm32R32Handler.cs b/X86Disassembler/X86/Handlers/Cmp/CmpRm32R32Handler.cs new file mode 100644 index 0000000..c07c61c --- /dev/null +++ b/X86Disassembler/X86/Handlers/Cmp/CmpRm32R32Handler.cs @@ -0,0 +1,179 @@ +namespace X86Disassembler.X86.Handlers.Cmp; + +/// +/// Handler for CMP r/m32, r32 instruction (0x39) +/// +public class CmpRm32R32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the CmpRm32R32Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public CmpRm32R32Handler(byte[] codeBuffer, InstructionDecoder decoder, int length) + : base(codeBuffer, decoder, length) + { + } + + /// + /// 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 == 0x39; + } + + /// + /// Decodes a CMP 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 mnemonic + instruction.Mnemonic = "cmp"; + + int position = Decoder.GetPosition(); + + if (position >= Length) + { + return false; + } + + // Read the ModR/M byte + byte modRM = CodeBuffer[position++]; + + // Extract the fields from the ModR/M byte + byte mod = (byte)((modRM & 0xC0) >> 6); // Top 2 bits + byte reg = (byte)((modRM & 0x38) >> 3); // Middle 3 bits + byte rm = (byte)(modRM & 0x07); // Bottom 3 bits + + // Get the register name for the reg field + string regName = GetRegisterName(reg); + + // Handle the different addressing modes + string rmOperand; + + if (mod == 3) // Direct register addressing + { + // Get the register name for the r/m field + rmOperand = GetRegisterName(rm); + } + else // Memory addressing + { + // Handle SIB byte if needed + if (mod != 3 && rm == 4) // SIB byte present + { + if (position >= Length) + { + return false; + } + + byte sib = CodeBuffer[position++]; + + // Extract the fields from the SIB byte + byte scale = (byte)((sib & 0xC0) >> 6); + byte index = (byte)((sib & 0x38) >> 3); + byte base_ = (byte)(sib & 0x07); + + // TODO: Handle SIB byte properly + rmOperand = $"[complex addressing]"; + } + else if (mod == 0 && rm == 5) // Displacement only addressing + { + if (position + 3 >= Length) + { + return false; + } + + // Read the 32-bit displacement + uint disp = (uint)(CodeBuffer[position] | + (CodeBuffer[position + 1] << 8) | + (CodeBuffer[position + 2] << 16) | + (CodeBuffer[position + 3] << 24)); + position += 4; + + rmOperand = $"[0x{disp:X8}]"; + } + else // Simple addressing modes + { + string baseReg = GetRegisterName(rm); + + if (mod == 0) // No displacement + { + rmOperand = $"[{baseReg}]"; + } + else // Displacement + { + uint disp; + + if (mod == 1) // 8-bit displacement + { + if (position >= Length) + { + return false; + } + + // Sign-extend the 8-bit displacement + sbyte dispByte = (sbyte)CodeBuffer[position++]; + disp = (uint)(int)dispByte; + + // Format the displacement + string dispStr = dispByte < 0 ? $"-0x{-dispByte:X2}" : $"0x{dispByte:X2}"; + rmOperand = $"[{baseReg}+{dispStr}]"; + } + else // 32-bit displacement + { + if (position + 3 >= Length) + { + return false; + } + + // Read the 32-bit displacement + disp = (uint)(CodeBuffer[position] | + (CodeBuffer[position + 1] << 8) | + (CodeBuffer[position + 2] << 16) | + (CodeBuffer[position + 3] << 24)); + position += 4; + + rmOperand = $"[{baseReg}+0x{disp:X8}]"; + } + } + } + } + + // Update the decoder position + Decoder.SetPosition(position); + + // Set the operands + instruction.Operands = $"{rmOperand}, {regName}"; + + return true; + } + + /// + /// Gets the register name for a register number + /// + /// The register number + /// The register name + private string GetRegisterName(byte regNum) + { + // 32-bit registers + switch (regNum) + { + case 0: return "eax"; + case 1: return "ecx"; + case 2: return "edx"; + case 3: return "ebx"; + case 4: return "esp"; + case 5: return "ebp"; + case 6: return "esi"; + case 7: return "edi"; + default: return "??"; + } + } +} diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs index 6f00136..24f9245 100644 --- a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs +++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs @@ -209,6 +209,7 @@ public class InstructionHandlerFactory { // Add Cmp handlers _handlers.Add(new CmpR32Rm32Handler(_codeBuffer, _decoder, _length)); + _handlers.Add(new CmpRm32R32Handler(_codeBuffer, _decoder, _length)); _handlers.Add(new CmpImmWithRm8Handler(_codeBuffer, _decoder, _length)); _handlers.Add(new CmpAlImmHandler(_codeBuffer, _decoder, _length)); diff --git a/X86DisassemblerTests/CmpInstructionHandlerTests.cs b/X86DisassemblerTests/CmpInstructionHandlerTests.cs index f95e660..a4ecb84 100644 --- a/X86DisassemblerTests/CmpInstructionHandlerTests.cs +++ b/X86DisassemblerTests/CmpInstructionHandlerTests.cs @@ -51,4 +51,46 @@ public class CmpInstructionHandlerTests Assert.Equal("cmp", instruction.Mnemonic); Assert.Equal("al, 0xFF", instruction.Operands); } + + /// + /// Tests the CmpRm32R32Handler for decoding CMP r/m32, r32 instructions with register operands + /// + [Fact] + public void CmpRm32R32Handler_DecodesCmpRm32R32_WithRegisterOperands() + { + // Arrange + // CMP ECX, EAX (39 C1) - ModR/M byte C1 = 11 000 001 (mod=3, reg=0, rm=1) + // mod=3 means direct register addressing, reg=0 is EAX, rm=1 is ECX + byte[] codeBuffer = new byte[] { 0x39, 0xC1 }; + var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length); + + // Act + var instruction = decoder.DecodeInstruction(); + + // Assert + Assert.NotNull(instruction); + Assert.Equal("cmp", instruction.Mnemonic); + Assert.Equal("ecx, eax", instruction.Operands); + } + + /// + /// Tests the CmpRm32R32Handler for decoding CMP r/m32, r32 instructions with memory operands + /// + [Fact] + public void CmpRm32R32Handler_DecodesCmpRm32R32_WithMemoryOperands() + { + // Arrange + // CMP [EBX+0x10], EDX (39 53 10) - ModR/M byte 53 = 01 010 011 (mod=1, reg=2, rm=3) + // mod=1 means memory addressing with 8-bit displacement, reg=2 is EDX, rm=3 is EBX + byte[] codeBuffer = new byte[] { 0x39, 0x53, 0x10 }; + var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length); + + // Act + var instruction = decoder.DecodeInstruction(); + + // Assert + Assert.NotNull(instruction); + Assert.Equal("cmp", instruction.Mnemonic); + Assert.Equal("[ebx+0x10], edx", instruction.Operands); + } }