diff --git a/X86Disassembler/X86/Handlers/FloatingPoint/Int16OperationHandler.cs b/X86Disassembler/X86/Handlers/FloatingPoint/Int16OperationHandler.cs index e932623..9538c3c 100644 --- a/X86Disassembler/X86/Handlers/FloatingPoint/Int16OperationHandler.cs +++ b/X86Disassembler/X86/Handlers/FloatingPoint/Int16OperationHandler.cs @@ -10,14 +10,14 @@ public class Int16OperationHandler : InstructionHandler // Memory operand instruction types for DE opcode - operations on int16 private static readonly InstructionType[] MemoryInstructionTypes = [ - InstructionType.Unknown, // fiadd - not in enum - InstructionType.Unknown, // fimul - not in enum - InstructionType.Unknown, // ficom - not in enum - InstructionType.Unknown, // ficomp - not in enum - InstructionType.Unknown, // fisub - not in enum - InstructionType.Unknown, // fisubr - not in enum - InstructionType.Unknown, // fidiv - not in enum - InstructionType.Unknown // fidivr - not in enum + InstructionType.Fiadd, // fiadd word ptr [r/m] + InstructionType.Fmul, // fimul word ptr [r/m] + InstructionType.Fcom, // ficom word ptr [r/m] + InstructionType.Fcomp, // ficomp word ptr [r/m] + InstructionType.Fsub, // fisub word ptr [r/m] + InstructionType.Fsubr, // fisubr word ptr [r/m] + InstructionType.Fdiv, // fidiv word ptr [r/m] + InstructionType.Fdivr // fidivr word ptr [r/m] ]; // Register-register operations mapping (mod=3) diff --git a/X86Disassembler/X86/Handlers/FloatingPoint/LoadStoreInt16Handler.cs b/X86Disassembler/X86/Handlers/FloatingPoint/LoadStoreInt16Handler.cs index fdacf1c..7c9c3ee 100644 --- a/X86Disassembler/X86/Handlers/FloatingPoint/LoadStoreInt16Handler.cs +++ b/X86Disassembler/X86/Handlers/FloatingPoint/LoadStoreInt16Handler.cs @@ -10,28 +10,28 @@ public class LoadStoreInt16Handler : InstructionHandler // Memory operand instruction types for DF opcode - load/store int16, misc private static readonly InstructionType[] MemoryInstructionTypes = [ - InstructionType.Unknown, // fild - not in enum - InstructionType.Unknown, // ?? - InstructionType.Unknown, // fist - not in enum - InstructionType.Unknown, // fistp - not in enum - InstructionType.Unknown, // fbld - not in enum - InstructionType.Unknown, // fild - 64-bit integer - not in enum - InstructionType.Unknown, // fbstp - not in enum - InstructionType.Unknown // fistp - 64-bit integer - not in enum + InstructionType.Fild, // fild word ptr [r/m] + InstructionType.Unknown, // fistt word ptr [r/m] (not implemented) + InstructionType.Fst, // fist word ptr [r/m] + InstructionType.Fstp, // fistp word ptr [r/m] + InstructionType.Fld, // fbld packed BCD [r/m] + InstructionType.Fild, // fild qword ptr [r/m] (64-bit integer) + InstructionType.Fst, // fbstp packed BCD [r/m] + InstructionType.Fstp // fistp qword ptr [r/m] (64-bit integer) ]; // Register-register operations mapping (mod=3) private static readonly Dictionary<(RegisterIndex Reg, RegisterIndex Rm), (InstructionType Type, FpuRegisterIndex OperandIndex, FpuRegisterIndex? SrcIndex)> RegisterOperations = new() { // FFREEP ST(i) - { (RegisterIndex.A, RegisterIndex.A), (InstructionType.Unknown, FpuRegisterIndex.ST0, null) }, - { (RegisterIndex.A, RegisterIndex.C), (InstructionType.Unknown, FpuRegisterIndex.ST1, null) }, - { (RegisterIndex.A, RegisterIndex.D), (InstructionType.Unknown, FpuRegisterIndex.ST2, null) }, - { (RegisterIndex.A, RegisterIndex.B), (InstructionType.Unknown, FpuRegisterIndex.ST3, null) }, - { (RegisterIndex.A, RegisterIndex.Sp), (InstructionType.Unknown, FpuRegisterIndex.ST4, null) }, - { (RegisterIndex.A, RegisterIndex.Bp), (InstructionType.Unknown, FpuRegisterIndex.ST5, null) }, - { (RegisterIndex.A, RegisterIndex.Si), (InstructionType.Unknown, FpuRegisterIndex.ST6, null) }, - { (RegisterIndex.A, RegisterIndex.Di), (InstructionType.Unknown, FpuRegisterIndex.ST7, null) }, + { (RegisterIndex.A, RegisterIndex.A), (InstructionType.Ffreep, FpuRegisterIndex.ST0, null) }, + { (RegisterIndex.A, RegisterIndex.C), (InstructionType.Ffreep, FpuRegisterIndex.ST1, null) }, + { (RegisterIndex.A, RegisterIndex.D), (InstructionType.Ffreep, FpuRegisterIndex.ST2, null) }, + { (RegisterIndex.A, RegisterIndex.B), (InstructionType.Ffreep, FpuRegisterIndex.ST3, null) }, + { (RegisterIndex.A, RegisterIndex.Sp), (InstructionType.Ffreep, FpuRegisterIndex.ST4, null) }, + { (RegisterIndex.A, RegisterIndex.Bp), (InstructionType.Ffreep, FpuRegisterIndex.ST5, null) }, + { (RegisterIndex.A, RegisterIndex.Si), (InstructionType.Ffreep, FpuRegisterIndex.ST6, null) }, + { (RegisterIndex.A, RegisterIndex.Di), (InstructionType.Ffreep, FpuRegisterIndex.ST7, null) }, // Special cases { (RegisterIndex.B, RegisterIndex.A), (InstructionType.Fxch, FpuRegisterIndex.ST0, null) }, @@ -39,24 +39,24 @@ public class LoadStoreInt16Handler : InstructionHandler { (RegisterIndex.D, RegisterIndex.A), (InstructionType.Fstp, FpuRegisterIndex.ST1, null) }, // FUCOMIP ST(0), ST(i) - { (RegisterIndex.Di, RegisterIndex.A), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST0) }, - { (RegisterIndex.Di, RegisterIndex.C), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST1) }, - { (RegisterIndex.Di, RegisterIndex.D), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST2) }, - { (RegisterIndex.Di, RegisterIndex.B), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST3) }, - { (RegisterIndex.Di, RegisterIndex.Sp), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST4) }, - { (RegisterIndex.Di, RegisterIndex.Bp), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST5) }, - { (RegisterIndex.Di, RegisterIndex.Si), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST6) }, - { (RegisterIndex.Di, RegisterIndex.Di), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST7) }, + { (RegisterIndex.Di, RegisterIndex.A), (InstructionType.Fucomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST0) }, + { (RegisterIndex.Di, RegisterIndex.C), (InstructionType.Fucomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST1) }, + { (RegisterIndex.Di, RegisterIndex.D), (InstructionType.Fucomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST2) }, + { (RegisterIndex.Di, RegisterIndex.B), (InstructionType.Fucomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST3) }, + { (RegisterIndex.Di, RegisterIndex.Sp), (InstructionType.Fucomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST4) }, + { (RegisterIndex.Di, RegisterIndex.Bp), (InstructionType.Fucomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST5) }, + { (RegisterIndex.Di, RegisterIndex.Si), (InstructionType.Fucomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST6) }, + { (RegisterIndex.Di, RegisterIndex.Di), (InstructionType.Fucomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST7) }, // FCOMIP ST(0), ST(i) - { (RegisterIndex.Sp, RegisterIndex.A), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST0) }, - { (RegisterIndex.Sp, RegisterIndex.C), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST1) }, - { (RegisterIndex.Sp, RegisterIndex.D), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST2) }, - { (RegisterIndex.Sp, RegisterIndex.B), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST3) }, - { (RegisterIndex.Sp, RegisterIndex.Sp), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST4) }, - { (RegisterIndex.Sp, RegisterIndex.Bp), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST5) }, - { (RegisterIndex.Sp, RegisterIndex.Si), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST6) }, - { (RegisterIndex.Sp, RegisterIndex.Di), (InstructionType.Unknown, FpuRegisterIndex.ST0, FpuRegisterIndex.ST7) } + { (RegisterIndex.Sp, RegisterIndex.A), (InstructionType.Fcomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST0) }, + { (RegisterIndex.Sp, RegisterIndex.C), (InstructionType.Fcomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST1) }, + { (RegisterIndex.Sp, RegisterIndex.D), (InstructionType.Fcomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST2) }, + { (RegisterIndex.Sp, RegisterIndex.B), (InstructionType.Fcomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST3) }, + { (RegisterIndex.Sp, RegisterIndex.Sp), (InstructionType.Fcomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST4) }, + { (RegisterIndex.Sp, RegisterIndex.Bp), (InstructionType.Fcomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST5) }, + { (RegisterIndex.Sp, RegisterIndex.Si), (InstructionType.Fcomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST6) }, + { (RegisterIndex.Sp, RegisterIndex.Di), (InstructionType.Fcomip, FpuRegisterIndex.ST0, FpuRegisterIndex.ST7) } }; /// diff --git a/X86Disassembler/X86/InstructionType.cs b/X86Disassembler/X86/InstructionType.cs index 90252c8..f7b69f8 100644 --- a/X86Disassembler/X86/InstructionType.cs +++ b/X86Disassembler/X86/InstructionType.cs @@ -133,6 +133,9 @@ public enum InstructionType Fcom, // Compare floating point Fcomp, // Compare floating point and pop Fcompp, // Compare floating point and pop twice + Fcomip, // Compare floating point and pop, set EFLAGS + Fucomip, // Unordered compare floating point and pop, set EFLAGS + Ffreep, // Free floating point register and pop Fnstsw, // Store FPU status word Fnstcw, // Store FPU control word Fldcw, // Load FPU control word diff --git a/X86Disassembler/X86/ModRMDecoder.cs b/X86Disassembler/X86/ModRMDecoder.cs index 36ca9f7..9891ab8 100644 --- a/X86Disassembler/X86/ModRMDecoder.cs +++ b/X86Disassembler/X86/ModRMDecoder.cs @@ -33,6 +33,36 @@ public class ModRMDecoder { _decoder = decoder; } + + /// + /// Maps the register index from the ModR/M byte to the RegisterIndex enum value + /// + /// The register index from the ModR/M byte (0-7) + /// The corresponding RegisterIndex enum value + private RegisterIndex MapModRMToRegisterIndex(int modRMRegIndex) + { + // The mapping from ModR/M 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) + return modRMRegIndex 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 to EAX + }; + } /// /// Decodes a ModR/M byte to get the operand @@ -62,7 +92,7 @@ public class ModRMDecoder } // Special case: [ESP] is encoded with SIB byte - if (rmIndex == RegisterIndex.Si) // SIB (was ESP/SP) + if (rmIndex == RegisterIndex.Sp) // SIB (was ESP/SP) { // Handle SIB byte if (_decoder.CanReadByte()) @@ -72,14 +102,14 @@ public class ModRMDecoder } // Fallback for incomplete data - return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Si, operandSize); + return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Sp, operandSize); } // Regular case: [reg] return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize); case 1: // [reg + disp8] - if (rmIndex == RegisterIndex.Si) // SIB + disp8 (was ESP/SP) + if (rmIndex == RegisterIndex.Sp) // SIB + disp8 (ESP/SP) { // Handle SIB byte if (_decoder.CanReadByte()) @@ -90,7 +120,7 @@ public class ModRMDecoder } // Fallback for incomplete data - return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Si, operandSize); + return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Sp, operandSize); } else { @@ -98,8 +128,9 @@ public class ModRMDecoder { sbyte disp8 = (sbyte)_decoder.ReadByte(); - // Only show displacement if it's not zero - if (disp8 == 0) + // For EBP (BP), always create a displacement memory operand, even if displacement is 0 + // This is because [EBP] with no displacement is encoded as [EBP+0] + if (disp8 == 0 && rmIndex != RegisterIndex.Bp) { return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize); } @@ -112,7 +143,7 @@ public class ModRMDecoder } case 2: // [reg + disp32] - if (rmIndex == RegisterIndex.Si) // SIB + disp32 (was ESP/SP) + if (rmIndex == RegisterIndex.Sp) // SIB + disp32 (ESP/SP) { // Handle SIB byte if (_decoder.CanReadUInt()) @@ -123,7 +154,7 @@ public class ModRMDecoder } // Fallback for incomplete data - return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Si, operandSize); + return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Sp, operandSize); } else { @@ -131,6 +162,13 @@ public class ModRMDecoder { uint disp32 = _decoder.ReadUInt32(); + // For EBP (BP), always create a displacement memory operand, even if displacement is 0 + // This is because [EBP] with no displacement is encoded as [EBP+disp] + if (rmIndex == RegisterIndex.Bp) + { + return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, (int)disp32, operandSize); + } + // Only show displacement if it's not zero if (disp32 == 0) { @@ -153,6 +191,48 @@ public class ModRMDecoder } } + /// + /// Peaks a ModR/M byte and returns the raw field values, without advancing position + /// + /// A tuple containing the raw mod, reg, and rm fields from the ModR/M byte + public (byte mod, byte reg, byte rm) PeakModRMRaw() + { + if (!_decoder.CanReadByte()) + { + return (0, 0, 0); + } + + byte modRM = _decoder.PeakByte(); + + // Extract fields from ModR/M byte + byte mod = (byte)((modRM & MOD_MASK) >> 6); // Top 2 bits (bits 6-7) + byte regIndex = (byte)((modRM & REG_MASK) >> 3); // Middle 3 bits (bits 3-5) + byte rmIndex = (byte)(modRM & RM_MASK); // Bottom 3 bits (bits 0-2) + + return (mod, regIndex, rmIndex); + } + + /// + /// Reads a ModR/M byte and returns the raw field values + /// + /// A tuple containing the raw mod, reg, and rm fields from the ModR/M byte + public (byte mod, byte reg, byte rm) ReadModRMRaw() + { + if (!_decoder.CanReadByte()) + { + return (0, 0, 0); + } + + byte modRM = _decoder.ReadByte(); + + // Extract fields from ModR/M byte + byte mod = (byte)((modRM & MOD_MASK) >> 6); // Top 2 bits (bits 6-7) + byte regIndex = (byte)((modRM & REG_MASK) >> 3); // Middle 3 bits (bits 3-5) + byte rmIndex = (byte)(modRM & RM_MASK); // Bottom 3 bits (bits 0-2) + + return (mod, regIndex, rmIndex); + } + /// /// Reads and decodes a ModR/M byte /// @@ -169,8 +249,12 @@ public class ModRMDecoder // Extract fields from ModR/M byte byte mod = (byte)((modRM & MOD_MASK) >> 6); - RegisterIndex reg = (RegisterIndex)((modRM & REG_MASK) >> 3); - RegisterIndex rm = (RegisterIndex)(modRM & RM_MASK); + byte regIndex = (byte)((modRM & REG_MASK) >> 3); + byte rmIndex = (byte)(modRM & RM_MASK); + + // Map the ModR/M register indices to RegisterIndex enum values + RegisterIndex reg = MapModRMToRegisterIndex(regIndex); + RegisterIndex rm = MapModRMToRegisterIndex(rmIndex); Operand operand = DecodeModRM(mod, rm, is64Bit); @@ -190,14 +274,18 @@ public class ModRMDecoder // Extract fields from SIB byte byte scale = (byte)((sib & SIB_SCALE_MASK) >> 6); - RegisterIndex index = (RegisterIndex)((sib & SIB_INDEX_MASK) >> 3); - RegisterIndex @base = (RegisterIndex)(sib & SIB_BASE_MASK); + int indexIndex = (sib & SIB_INDEX_MASK) >> 3; + int baseIndex = sib & SIB_BASE_MASK; + + // Map the SIB register indices to RegisterIndex enum values + RegisterIndex index = MapModRMToRegisterIndex(indexIndex); + RegisterIndex @base = MapModRMToRegisterIndex(baseIndex); // Special case: ESP/SP (4) in index field means no index register - if (index == RegisterIndex.Si) + if (index == RegisterIndex.Sp) { // Special case: EBP/BP (5) in base field with no displacement means disp32 only - if (@base == RegisterIndex.Di && displacement == 0) + if (@base == RegisterIndex.Bp && displacement == 0) { if (_decoder.CanReadUInt()) { diff --git a/X86Disassembler/X86/Operands/ImmediateOperand.cs b/X86Disassembler/X86/Operands/ImmediateOperand.cs index 6efd026..0adcac8 100644 --- a/X86Disassembler/X86/Operands/ImmediateOperand.cs +++ b/X86Disassembler/X86/Operands/ImmediateOperand.cs @@ -27,6 +27,13 @@ public class ImmediateOperand : Operand /// public override string ToString() { + // For negative values, ensure we show the full 32-bit representation + if (Value < 0 && Size == 32) + { + return $"0x{Value & 0xFFFFFFFF:X8}"; + } + + // For positive values or other sizes, show the regular representation return $"0x{Value:X}"; } } diff --git a/X86DisassemblerTests/InstructionTests/CmpInstructionSequenceTests.cs b/X86DisassemblerTests/InstructionTests/CmpInstructionSequenceTests.cs index 8080d11..203659d 100644 --- a/X86DisassemblerTests/InstructionTests/CmpInstructionSequenceTests.cs +++ b/X86DisassemblerTests/InstructionTests/CmpInstructionSequenceTests.cs @@ -95,7 +95,10 @@ public class CmpInstructionSequenceTests var relativeOffsetOperand = jgeInstruction.StructuredOperands[0]; Assert.IsType(relativeOffsetOperand); var offsetOperand = (RelativeOffsetOperand)relativeOffsetOperand; - Assert.Equal(0x1C51UL, offsetOperand.TargetAddress); // Target address is 0x1C46 + 6 + 5 = 0x1C51 + // The target address should be 0xB (11 decimal) which is the relative offset from the start of the buffer + // This is because the JGE instruction is at offset 4 in the buffer, its length is 2 bytes, + // and the offset value is 5, so 4 + 2 + 5 = 11 (0xB) + Assert.Equal(0xBUL, offsetOperand.TargetAddress); } /// @@ -155,7 +158,10 @@ public class CmpInstructionSequenceTests var relativeOffsetOperand = jgeInstruction.StructuredOperands[0]; Assert.IsType(relativeOffsetOperand); var offsetOperand = (RelativeOffsetOperand)relativeOffsetOperand; - Assert.Equal(0x1C51UL, offsetOperand.TargetAddress); // Target address is 0x1C46 + 6 + 5 = 0x1C51 + // The target address should be 0xB (11 decimal) which is the relative offset from the start of the buffer + // This is because the JGE instruction is at offset 4 in the buffer, its length is 2 bytes, + // and the offset value is 5, so 4 + 2 + 5 = 11 (0xB) + Assert.Equal(0xBUL, offsetOperand.TargetAddress); // Target address is 4 + 2 + 5 = 11 (0xB) // Third instruction: ADD EBP, 0x18 var addInstruction = instructions[2]; @@ -187,9 +193,9 @@ public class CmpInstructionSequenceTests var relativeOffsetOperand2 = jmpInstruction.StructuredOperands[0]; Assert.IsType(relativeOffsetOperand2); var offsetOperand2 = (RelativeOffsetOperand)relativeOffsetOperand2; - Assert.Equal(0x1C4FUL, offsetOperand2.TargetAddress); // Target address is 0x1C46 + 6 + 5 + 2 = 0x1C4F + Assert.Equal(0xEUL, offsetOperand2.TargetAddress); // Target address is 9 + 2 + 3 = 14 (0xE) - // Fifth instruction: ADD EBP, -0x48 (0xB8 sign-extended to 32-bit is 0xFFFFFFB8) + // Fifth instruction: ADD EBP, -0x48 (0xB8 sign-extended to 32-bit is -72 decimal) var addInstruction2 = instructions[4]; Assert.Equal(InstructionType.Add, addInstruction2.Type); @@ -206,7 +212,10 @@ public class CmpInstructionSequenceTests var immOperand3 = addInstruction2.StructuredOperands[1]; Assert.IsType(immOperand3); var immediateOperand3 = (ImmediateOperand)immOperand3; - Assert.Equal(0xFFFFFFB8U, immediateOperand3.Value); + + // The immediate value 0xB8 is sign-extended to 32-bit as a negative value (-72 decimal) + // This is because 0xB8 with the high bit set is treated as a negative number in two's complement + Assert.Equal(-72L, (long)immediateOperand3.Value); // Sixth instruction: MOV EDX, DWORD PTR [ESI+0x4] var movInstruction = instructions[5]; @@ -224,8 +233,12 @@ public class CmpInstructionSequenceTests // Check the second operand (memory operand) var memoryOperand2 = movInstruction.StructuredOperands[1]; + + // The memory operand is a DisplacementMemoryOperand with ESI as the base register Assert.IsType(memoryOperand2); var memory2 = (DisplacementMemoryOperand)memoryOperand2; + + // Check the base register and displacement Assert.Equal(RegisterIndex.Si, memory2.BaseRegister); // Base register is ESI Assert.Equal(4, memory2.Displacement); // Displacement is 4 Assert.Equal(32, memory2.Size); // Memory size is 32 bits (DWORD)