0
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-06-18 15:49:47 +03:00

unbreak tests

This commit is contained in:
bird_egop
2025-04-14 23:08:52 +03:00
parent 685eeda03d
commit 9117830ff1
41 changed files with 3820 additions and 736 deletions

View File

@ -79,26 +79,32 @@ public enum InstructionType
MovsB,
MovsW,
MovsD,
Movs = MovsD, // Alias for MovsD
CmpsB,
CmpsW,
CmpsD,
StosB,
StosW,
StosD,
Stos = StosB, // Alias for StosB
ScasB, // Scan string byte
ScasW, // Scan string word
ScasD, // Scan string dword
Scas = ScasB, // Alias for ScasB
LodsB, // Load string byte
LodsW, // Load string word
LodsD, // Load string dword
Lods = LodsD, // Alias for LodsD
// REP prefixed instructions
Rep, // REP prefix
RepE, // REPE/REPZ prefix
RepNE, // REPNE/REPNZ prefix
RepneScas = RepNE, // Alias for RepNE
RepMovsB, // REP MOVSB
RepMovsW, // REP MOVSW
RepMovsD, // REP MOVSD
RepMovs = RepMovsD, // Alias for RepMovsD
RepeCmpsB, // REPE CMPSB
RepeCmpsW, // REPE CMPSW
RepeCmpsD, // REPE CMPSD
@ -117,6 +123,8 @@ public enum InstructionType
Fst, // Store floating point value
Fstp, // Store floating point value and pop
Fadd, // Add floating point
Fiadd, // Add integer to floating point
Fild, // Load integer to floating point
Fsub, // Subtract floating point
Fsubr, // Subtract floating point reversed
Fmul, // Multiply floating point

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -24,8 +25,23 @@ public class AdcInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("adc", instruction.Mnemonic);
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Adc, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -45,7 +61,22 @@ public class AdcInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("adc", instruction.Mnemonic);
Assert.Equal("eax, 0x00000042", instruction.Operands);
Assert.Equal(InstructionType.Adc, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000042U, immediateOperand.Value);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -23,7 +24,21 @@ public class AddEaxImmHandlerTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("add", instruction.Mnemonic);
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Add, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,24 @@ public class AddInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("add", instructions[0].Mnemonic);
Assert.Equal("esi, ebp", instructions[0].Operands);
Assert.Equal(InstructionType.Add, instructions[0].Type);
// Check that we have two operands
Assert.Equal(2, instructions[0].StructuredOperands.Count);
// Check the first operand (ESI)
var esiOperand = instructions[0].StructuredOperands[0];
Assert.IsType<RegisterOperand>(esiOperand);
var registerOperand1 = (RegisterOperand)esiOperand;
Assert.Equal(RegisterIndex.Si, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (ESI)
// Check the second operand (EBP)
var ebpOperand = instructions[0].StructuredOperands[1];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand2 = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EBP)
}
/// <summary>
@ -41,7 +58,23 @@ public class AddInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("add", instructions[0].Mnemonic);
Assert.Equal("eax, dword ptr [eax]", instructions[0].Operands);
Assert.Equal(InstructionType.Add, instructions[0].Type);
// Check that we have two operands
Assert.Equal(2, instructions[0].StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instructions[0].StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand)
var memoryOperand = instructions[0].StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var memory = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, memory.BaseRegister); // Base register is EAX
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,24 @@ public class AddRm32R32Tests
// Assert
Assert.Single(instructions);
Assert.Equal("add", instructions[0].Mnemonic);
Assert.Equal("ecx, eax", instructions[0].Operands);
Assert.Equal(InstructionType.Add, instructions[0].Type);
// Check that we have two operands
Assert.Equal(2, instructions[0].StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instructions[0].StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand1 = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (EAX)
var eaxOperand = instructions[0].StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand2 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -41,7 +58,23 @@ public class AddRm32R32Tests
// Assert
Assert.Single(instructions);
Assert.Equal("add", instructions[0].Mnemonic);
Assert.Equal("dword ptr [ecx], eax", instructions[0].Operands);
Assert.Equal(InstructionType.Add, instructions[0].Type);
// Check that we have two operands
Assert.Equal(2, instructions[0].StructuredOperands.Count);
// Check the first operand (memory operand)
var memoryOperand = instructions[0].StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var memory = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.C, memory.BaseRegister); // Base register is ECX
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
// Check the second operand (EAX)
var eaxOperand = instructions[0].StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -24,8 +25,23 @@ public class AndInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("and", instruction.Mnemonic);
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.And, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -45,15 +61,30 @@ public class AndInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("and", instruction.Mnemonic);
Assert.Equal("eax, 0x00000042", instruction.Operands);
Assert.Equal(InstructionType.And, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000042U, immediateOperand.Value);
}
/// <summary>
/// Tests the AND r32, r/m32 instruction
/// Tests the AndRegMemHandler for decoding AND r32, r/m32 instruction
/// </summary>
[Fact]
public void And_DecodesAndR32Rm32_Correctly()
public void AndRegMemHandler_DecodesAndR32Rm32_Correctly()
{
// Arrange
// AND EAX, ECX (23 C1) - ModR/M byte C1 = 11 000 001 (mod=3, reg=0, rm=1)
@ -66,15 +97,31 @@ public class AndInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("and", instruction.Mnemonic);
Assert.Equal("eax, ecx", instruction.Operands);
Assert.Equal(InstructionType.And, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (ECX)
var ecxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ecxOperand);
var ecxRegisterOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, ecxRegisterOperand.Register);
Assert.Equal(32, ecxRegisterOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
/// Tests the AND r/m32, r32 instruction
/// Tests the AndMemRegHandler for decoding AND r/m32, r32 instruction
/// </summary>
[Fact]
public void And_DecodesAndRm32R32_Correctly()
public void AndMemRegHandler_DecodesAndRm32R32_Correctly()
{
// Arrange
// AND ECX, EAX (21 C1) - ModR/M byte C1 = 11 000 001 (mod=3, reg=0, rm=1)
@ -87,7 +134,23 @@ public class AndInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("and", instruction.Mnemonic);
Assert.Equal("ecx, eax", instruction.Operands);
Assert.Equal(InstructionType.And, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var ecxRegisterOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, ecxRegisterOperand.Register);
Assert.Equal(32, ecxRegisterOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -24,8 +25,17 @@ public class ArithmeticUnaryTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("div", instruction.Mnemonic);
Assert.Equal("ecx", instruction.Operands);
Assert.Equal(InstructionType.Div, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -45,8 +55,17 @@ public class ArithmeticUnaryTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("idiv", instruction.Mnemonic);
Assert.Equal("ecx", instruction.Operands);
Assert.Equal(InstructionType.IDiv, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -66,8 +85,17 @@ public class ArithmeticUnaryTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("mul", instruction.Mnemonic);
Assert.Equal("ecx", instruction.Operands);
Assert.Equal(InstructionType.Mul, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -87,8 +115,17 @@ public class ArithmeticUnaryTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("imul", instruction.Mnemonic);
Assert.Equal("ecx", instruction.Operands);
Assert.Equal(InstructionType.IMul, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -108,8 +145,17 @@ public class ArithmeticUnaryTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("neg", instruction.Mnemonic);
Assert.Equal("ecx", instruction.Operands);
Assert.Equal(InstructionType.Neg, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -129,7 +175,16 @@ public class ArithmeticUnaryTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("not", instruction.Mnemonic);
Assert.Equal("ecx", instruction.Operands);
Assert.Equal(InstructionType.Not, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -23,7 +24,17 @@ public class CallInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("call", instruction.Mnemonic);
Assert.Equal("0x1234567D", instruction.Operands); // Current position (5) + offset (0x12345678) = 0x1234567D
Assert.Equal(InstructionType.Call, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check that the operand is a relative offset operand
var operand = instruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
var relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(0x1234567DUL, relativeOffsetOperand.TargetAddress); // Current position (5) + offset (0x12345678) = 0x1234567D
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,18 @@ public class CallRm32Tests
// Assert
Assert.Single(instructions);
Assert.Equal("call", instructions[0].Mnemonic);
Assert.Equal("ebx", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Call, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EBX)
var ebxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBX)
}
/// <summary>
@ -41,7 +52,17 @@ public class CallRm32Tests
// Assert
Assert.Single(instructions);
Assert.Equal("call", instructions[0].Mnemonic);
Assert.Equal("dword ptr [eax]", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Call, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (memory operand)
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var memory = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, memory.BaseRegister); // Base register is EAX
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,23 @@ public class CmpImmWithRm8Tests
// Assert
Assert.Single(instructions);
Assert.Equal("cmp", instructions[0].Mnemonic);
Assert.Equal("cl, 0x02", instructions[0].Operands);
Assert.Equal(InstructionType.Cmp, instructions[0].Type);
// Check that we have two operands
Assert.Equal(2, instructions[0].StructuredOperands.Count);
// Check the first operand (CL)
var clOperand = instructions[0].StructuredOperands[0];
Assert.IsType<RegisterOperand>(clOperand);
var registerOperand = (RegisterOperand)clOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (CL)
// Check the second operand (immediate value)
var immOperand = instructions[0].StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x02U, immediateOperand.Value);
}
/// <summary>
@ -41,7 +57,22 @@ public class CmpImmWithRm8Tests
// Assert
Assert.Single(instructions);
Assert.Equal("cmp", instructions[0].Mnemonic);
Assert.Equal("byte ptr [ecx], 0x05", instructions[0].Operands);
Assert.Equal(InstructionType.Cmp, instructions[0].Type);
// Check that we have two operands
Assert.Equal(2, instructions[0].StructuredOperands.Count);
// Check the first operand (memory operand)
var memoryOperand = instructions[0].StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var memory = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.C, memory.BaseRegister); // Base register is ECX
Assert.Equal(8, memory.Size); // Memory size is 8 bits (BYTE)
// Check the second operand (immediate value)
var immOperand = instructions[0].StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x05U, immediateOperand.Value);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,16 +17,32 @@ public class CmpInstructionHandlerTests
// Arrange
// CMP AL, 0x03 (3C 03)
byte[] codeBuffer = new byte[] { 0x3C, 0x03 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("cmp", instruction.Mnemonic);
// The handler should produce "al, 0xXX" as the operands
Assert.Equal("al, 0x03", instruction.Operands);
Assert.Equal(InstructionType.Cmp, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x03U, immediateOperand.Value);
}
/// <summary>
@ -37,56 +54,110 @@ public class CmpInstructionHandlerTests
// Arrange
// CMP AL, 0xFF (3C FF)
byte[] codeBuffer = new byte[] { 0x3C, 0xFF };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("cmp", instruction.Mnemonic);
Assert.Equal("al, 0xFF", instruction.Operands);
Assert.Equal(InstructionType.Cmp, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0xFFU, immediateOperand.Value);
}
/// <summary>
/// Tests the CmpRm32R32Handler for decoding CMP r/m32, r32 instructions with register operands
/// Tests the CmpRm32R32Handler for decoding CMP r32, r32 instructions
/// </summary>
[Fact]
public void CmpRm32R32Handler_DecodesCmpRm32R32_WithRegisterOperands()
public void CmpRm32R32Handler_DecodesCmpR32R32_Correctly()
{
// 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);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("cmp", instruction.Mnemonic);
Assert.Equal("ecx, eax", instruction.Operands);
Assert.Equal(InstructionType.Cmp, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var ecxRegisterOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, ecxRegisterOperand.Register);
Assert.Equal(32, ecxRegisterOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
/// Tests the CmpRm32R32Handler for decoding CMP r/m32, r32 instructions with memory operands
/// Tests the CmpRm32R32Handler for decoding CMP m32, r32 instructions
/// </summary>
[Fact]
public void CmpRm32R32Handler_DecodesCmpRm32R32_WithMemoryOperands()
public void CmpRm32R32Handler_DecodesCmpM32R32_Correctly()
{
// 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);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("cmp", instruction.Mnemonic);
Assert.Equal("[ebx+0x10], edx", instruction.Operands);
Assert.Equal(InstructionType.Cmp, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand ([EBX+0x10])
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var memoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.B, memoryOperand.BaseRegister);
Assert.Equal(0x10, memoryOperand.Displacement);
Assert.Equal(32, memoryOperand.Size); // Memory size is 32 bits (DWORD)
// Check the second operand (EDX)
var edxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(edxOperand);
var edxRegisterOperand = (RegisterOperand)edxOperand;
Assert.Equal(RegisterIndex.D, edxRegisterOperand.Register);
Assert.Equal(32, edxRegisterOperand.Size); // Validate that it's a 32-bit register (EDX)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -23,8 +24,25 @@ public class CmpInstructionSequenceTests
// Assert
Assert.Single(instructions);
Assert.Equal("cmp", instructions[0].Mnemonic);
Assert.Equal("byte ptr [ebp], 0x03", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Cmp, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand);
var memory = (DisplacementMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.Bp, memory.BaseRegister); // Base register is EBP
Assert.Equal(0, memory.Displacement); // Displacement is 0
Assert.Equal(8, memory.Size); // Memory size is 8 bits (BYTE)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x03U, immediateOperand.Value);
}
/// <summary>
@ -46,12 +64,38 @@ public class CmpInstructionSequenceTests
Assert.Equal(2, instructions.Count);
// First instruction: CMP BYTE PTR [EBP], 0x03
Assert.Equal("cmp", instructions[0].Mnemonic);
Assert.Equal("byte ptr [ebp], 0x03", instructions[0].Operands);
var cmpInstruction = instructions[0];
Assert.Equal(InstructionType.Cmp, cmpInstruction.Type);
// Check that we have two operands
Assert.Equal(2, cmpInstruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memoryOperand = cmpInstruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand);
var memory = (DisplacementMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.Bp, memory.BaseRegister); // Base register is EBP
Assert.Equal(0, memory.Displacement); // Displacement is 0
Assert.Equal(8, memory.Size); // Memory size is 8 bits (BYTE)
// Check the second operand (immediate value)
var immOperand = cmpInstruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x03U, immediateOperand.Value);
// Second instruction: JGE +5
Assert.Equal("jge", instructions[1].Mnemonic);
Assert.Equal("0x0000000B", instructions[1].Operands); // Base address is ignored, only relative offset matters
var jgeInstruction = instructions[1];
Assert.Equal(InstructionType.Jge, jgeInstruction.Type);
// Check that we have one operand
Assert.Single(jgeInstruction.StructuredOperands);
// Check the operand (relative offset)
var relativeOffsetOperand = jgeInstruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(relativeOffsetOperand);
var offsetOperand = (RelativeOffsetOperand)relativeOffsetOperand;
Assert.Equal(0x1C51UL, offsetOperand.TargetAddress); // Target address is 0x1C46 + 6 + 5 = 0x1C51
}
/// <summary>
@ -80,27 +124,110 @@ public class CmpInstructionSequenceTests
Assert.True(instructions.Count >= 5, $"Expected at least 5 instructions, but got {instructions.Count}");
// First instruction: CMP BYTE PTR [EBP], 0x03
Assert.Equal("cmp", instructions[0].Mnemonic);
Assert.Equal("byte ptr [ebp], 0x03", instructions[0].Operands);
var cmpInstruction = instructions[0];
Assert.Equal(InstructionType.Cmp, cmpInstruction.Type);
// Check that we have two operands
Assert.Equal(2, cmpInstruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memoryOperand = cmpInstruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand);
var memory = (DisplacementMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.Bp, memory.BaseRegister); // Base register is EBP
Assert.Equal(0, memory.Displacement); // Displacement is 0
Assert.Equal(8, memory.Size); // Memory size is 8 bits (BYTE)
// Check the second operand (immediate value)
var immOperand = cmpInstruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x03U, immediateOperand.Value);
// Second instruction: JGE +5
Assert.Equal("jge", instructions[1].Mnemonic);
Assert.Equal("0x0000000B", instructions[1].Operands); // Base address is ignored, only relative offset matters
var jgeInstruction = instructions[1];
Assert.Equal(InstructionType.Jge, jgeInstruction.Type);
// Check that we have one operand
Assert.Single(jgeInstruction.StructuredOperands);
// Check the operand (relative offset)
var relativeOffsetOperand = jgeInstruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(relativeOffsetOperand);
var offsetOperand = (RelativeOffsetOperand)relativeOffsetOperand;
Assert.Equal(0x1C51UL, offsetOperand.TargetAddress); // Target address is 0x1C46 + 6 + 5 = 0x1C51
// Third instruction: ADD EBP, 0x18
Assert.Equal("add", instructions[2].Mnemonic);
Assert.Equal("ebp, 0x00000018", instructions[2].Operands);
var addInstruction = instructions[2];
Assert.Equal(InstructionType.Add, addInstruction.Type);
// Check that we have two operands
Assert.Equal(2, addInstruction.StructuredOperands.Count);
// Check the first operand (register operand)
var registerOperand = addInstruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(registerOperand);
var register = (RegisterOperand)registerOperand;
Assert.Equal(RegisterIndex.Bp, register.Register); // Register is EBP
// Check the second operand (immediate value)
var immOperand2 = addInstruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand2);
var immediateOperand2 = (ImmediateOperand)immOperand2;
Assert.Equal(0x18U, immediateOperand2.Value);
// Fourth instruction: JMP +3
Assert.Equal("jmp", instructions[3].Mnemonic);
Assert.Equal("0x0000000E", instructions[3].Operands); // Base address is ignored, only relative offset matters
var jmpInstruction = instructions[3];
Assert.Equal(InstructionType.Jmp, jmpInstruction.Type);
// Check that we have one operand
Assert.Single(jmpInstruction.StructuredOperands);
// Check the operand (relative offset)
var relativeOffsetOperand2 = jmpInstruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(relativeOffsetOperand2);
var offsetOperand2 = (RelativeOffsetOperand)relativeOffsetOperand2;
Assert.Equal(0x1C4FUL, offsetOperand2.TargetAddress); // Target address is 0x1C46 + 6 + 5 + 2 = 0x1C4F
// Fifth instruction: ADD EBP, -0x48 (0xB8 sign-extended to 32-bit is 0xFFFFFFB8)
Assert.Equal("add", instructions[4].Mnemonic);
Assert.Equal("ebp, 0xFFFFFFB8", instructions[4].Operands);
var addInstruction2 = instructions[4];
Assert.Equal(InstructionType.Add, addInstruction2.Type);
// Check that we have two operands
Assert.Equal(2, addInstruction2.StructuredOperands.Count);
// Check the first operand (register operand)
var registerOperand2 = addInstruction2.StructuredOperands[0];
Assert.IsType<RegisterOperand>(registerOperand2);
var register2 = (RegisterOperand)registerOperand2;
Assert.Equal(RegisterIndex.Bp, register2.Register); // Register is EBP
// Check the second operand (immediate value)
var immOperand3 = addInstruction2.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand3);
var immediateOperand3 = (ImmediateOperand)immOperand3;
Assert.Equal(0xFFFFFFB8U, immediateOperand3.Value);
// Sixth instruction: MOV EDX, DWORD PTR [ESI+0x4]
Assert.Equal("mov", instructions[5].Mnemonic);
Assert.Equal("edx, dword ptr [esi+0x04]", instructions[5].Operands);
var movInstruction = instructions[5];
Assert.Equal(InstructionType.Mov, movInstruction.Type);
// Check that we have two operands
Assert.Equal(2, movInstruction.StructuredOperands.Count);
// Check the first operand (register operand)
var registerOperand3 = movInstruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(registerOperand3);
var register3 = (RegisterOperand)registerOperand3;
Assert.Equal(RegisterIndex.D, register3.Register); // Register is EDX
Assert.Equal(32, register3.Size); // Validate that it's a 32-bit register (EDX)
// Check the second operand (memory operand)
var memoryOperand2 = movInstruction.StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand2);
var memory2 = (DisplacementMemoryOperand)memoryOperand2;
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)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,24 @@ public class CmpInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("cmp", instructions[0].Mnemonic);
Assert.Equal("eax, edi", instructions[0].Operands);
Assert.Equal(InstructionType.Cmp, instructions[0].Type);
// Check that we have two operands
Assert.Equal(2, instructions[0].StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instructions[0].StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (EDI)
var ediOperand = instructions[0].StructuredOperands[1];
Assert.IsType<RegisterOperand>(ediOperand);
var registerOperand2 = (RegisterOperand)ediOperand;
Assert.Equal(RegisterIndex.Di, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EDI)
}
/// <summary>
@ -41,7 +58,23 @@ public class CmpInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("cmp", instructions[0].Mnemonic);
Assert.Equal("eax, dword ptr [eax]", instructions[0].Operands);
Assert.Equal(InstructionType.Cmp, instructions[0].Type);
// Check that we have two operands
Assert.Equal(2, instructions[0].StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instructions[0].StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand)
var memoryOperand = instructions[0].StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var memory = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, memory.BaseRegister); // Base register is EAX
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -17,15 +18,33 @@ public class DataTransferInstructionTests
// MOV EAX, ECX (8B 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[] { 0x8B, 0xC1 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, ecx", instruction.Operands);
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (ECX)
var ecxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ecxOperand);
var ecxRegisterOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, ecxRegisterOperand.Register);
Assert.Equal(32, ecxRegisterOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -38,15 +57,33 @@ public class DataTransferInstructionTests
// MOV ECX, EAX (89 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[] { 0x89, 0xC1 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("ecx, eax", instruction.Operands);
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var ecxRegisterOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, ecxRegisterOperand.Register);
Assert.Equal(32, ecxRegisterOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -58,15 +95,32 @@ public class DataTransferInstructionTests
// Arrange
// MOV EAX, 0x12345678 (B8 78 56 34 12)
byte[] codeBuffer = new byte[] { 0xB8, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (Immediate)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immImmediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678, immImmediateOperand.Value);
}
/// <summary>
@ -78,15 +132,32 @@ public class DataTransferInstructionTests
// Arrange
// MOV AL, 0x42 (B0 42) - Register is encoded in the low 3 bits of the opcode
byte[] codeBuffer = new byte[] { 0xB0, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("al, 0x42", instruction.Operands);
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var alRegisterOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, alRegisterOperand.Register);
Assert.Equal(8, alRegisterOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (Immediate)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immImmediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42, immImmediateOperand.Value);
}
/// <summary>
@ -98,15 +169,32 @@ public class DataTransferInstructionTests
// Arrange
// MOV EAX, [0x12345678] (A1 78 56 34 12)
byte[] codeBuffer = new byte[] { 0xA1, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, [0x12345678]", instruction.Operands);
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (Memory)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DirectMemoryOperand>(memOperand);
var directMemoryOperand = (DirectMemoryOperand)memOperand;
Assert.Equal(0x12345678, directMemoryOperand.Address);
}
/// <summary>
@ -118,15 +206,32 @@ public class DataTransferInstructionTests
// Arrange
// MOV [0x12345678], EAX (A3 78 56 34 12)
byte[] codeBuffer = new byte[] { 0xA3, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("[0x12345678], eax", instruction.Operands);
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (Memory)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<DirectMemoryOperand>(memOperand);
var directMemoryOperand = (DirectMemoryOperand)memOperand;
Assert.Equal(0x12345678, directMemoryOperand.Address);
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -139,15 +244,33 @@ public class DataTransferInstructionTests
// MOV EAX, [ECX+0x12345678] (8B 81 78 56 34 12) - ModR/M byte 81 = 10 000 001 (mod=2, reg=0, rm=1)
// mod=2 means memory addressing with 32-bit displacement, reg=0 is EAX, rm=1 is ECX
byte[] codeBuffer = new byte[] { 0x8B, 0x81, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, dword ptr [ecx+0x12345678]", instruction.Operands);
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (Memory)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(0x12345678, displacementMemoryOperand.Displacement);
Assert.Equal(RegisterIndex.C, displacementMemoryOperand.BaseRegister);
}
/// <summary>
@ -159,15 +282,26 @@ public class DataTransferInstructionTests
// Arrange
// PUSH EAX (50)
byte[] codeBuffer = new byte[] { 0x50 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("push", instruction.Mnemonic);
Assert.Equal("eax", instruction.Operands);
Assert.Equal(InstructionType.Push, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -179,15 +313,25 @@ public class DataTransferInstructionTests
// Arrange
// PUSH 0x12345678 (68 78 56 34 12)
byte[] codeBuffer = new byte[] { 0x68, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("push", instruction.Mnemonic);
Assert.Equal("0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Push, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (Immediate)
var immOperand = instruction.StructuredOperands[0];
Assert.IsType<ImmediateOperand>(immOperand);
var immImmediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678, immImmediateOperand.Value);
}
/// <summary>
@ -199,15 +343,25 @@ public class DataTransferInstructionTests
// Arrange
// PUSH 0x42 (6A 42)
byte[] codeBuffer = new byte[] { 0x6A, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("push", instruction.Mnemonic);
Assert.Equal("0x42", instruction.Operands);
Assert.Equal(InstructionType.Push, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (Immediate)
var immOperand = instruction.StructuredOperands[0];
Assert.IsType<ImmediateOperand>(immOperand);
var immImmediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42, immImmediateOperand.Value);
}
/// <summary>
@ -219,15 +373,26 @@ public class DataTransferInstructionTests
// Arrange
// POP ECX (59)
byte[] codeBuffer = new byte[] { 0x59 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("pop", instruction.Mnemonic);
Assert.Equal("ecx", instruction.Operands);
Assert.Equal(InstructionType.Pop, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var ecxRegisterOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, ecxRegisterOperand.Register);
Assert.Equal(32, ecxRegisterOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -239,15 +404,33 @@ public class DataTransferInstructionTests
// Arrange
// XCHG EAX, ECX (91) - Register is encoded in the low 3 bits of the opcode
byte[] codeBuffer = new byte[] { 0x91 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("xchg", instruction.Mnemonic);
Assert.Equal("eax, ecx", instruction.Operands);
Assert.Equal(InstructionType.Xchg, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (ECX)
var ecxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ecxOperand);
var ecxRegisterOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, ecxRegisterOperand.Register);
Assert.Equal(32, ecxRegisterOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -259,14 +442,18 @@ public class DataTransferInstructionTests
// Arrange
// NOP (90) - This is actually XCHG EAX, EAX which is treated as NOP
byte[] codeBuffer = new byte[] { 0x90 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("nop", instruction.Mnemonic);
Assert.Equal("", instruction.Operands);
Assert.Equal(InstructionType.Nop, instruction.Type);
// Check that we have no operands
Assert.Empty(instruction.StructuredOperands);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,18 @@ public class DecInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("dec", instructions[0].Mnemonic);
Assert.Equal("eax", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Dec, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -41,8 +52,18 @@ public class DecInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("dec", instructions[0].Mnemonic);
Assert.Equal("ecx", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Dec, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -60,7 +81,17 @@ public class DecInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("dec", instructions[0].Mnemonic);
Assert.Equal("edi", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Dec, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EDI)
var ediOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ediOperand);
var registerOperand = (RegisterOperand)ediOperand;
Assert.Equal(RegisterIndex.Di, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDI)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,26 @@ public class FloatingPointInstructionTests
// Arrange
// FNSTSW AX (DF E0)
byte[] codeBuffer = new byte[] { 0xDF, 0xE0 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fnstsw", instruction.Mnemonic);
Assert.Equal("ax", instruction.Operands);
Assert.Equal(InstructionType.Fnstsw, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (AX)
var axOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(axOperand);
var registerOperand = (RegisterOperand)axOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(16, registerOperand.Size); // Validate that it's a 16-bit register (AX)
}
/// <summary>
@ -36,15 +48,31 @@ public class FloatingPointInstructionTests
// Arrange
// FADD ST(0), ST(1) (D8 C1)
byte[] codeBuffer = new byte[] { 0xD8, 0xC1 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fadd", instruction.Mnemonic);
Assert.Equal("st(0), st(1)", instruction.Operands);
Assert.Equal(InstructionType.Fadd, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ST(0))
var st0Operand = instruction.StructuredOperands[0];
Assert.IsType<FPURegisterOperand>(st0Operand);
var fpuRegisterOperand1 = (FPURegisterOperand)st0Operand;
Assert.Equal(FpuRegisterIndex.ST0, fpuRegisterOperand1.RegisterIndex);
// Check the second operand (ST(1))
var st1Operand = instruction.StructuredOperands[1];
Assert.IsType<FPURegisterOperand>(st1Operand);
var fpuRegisterOperand2 = (FPURegisterOperand)st1Operand;
Assert.Equal(FpuRegisterIndex.ST1, fpuRegisterOperand2.RegisterIndex);
}
/// <summary>
@ -56,15 +84,26 @@ public class FloatingPointInstructionTests
// Arrange
// FADD dword ptr [eax] (D8 00)
byte[] codeBuffer = new byte[] { 0xD8, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fadd", instruction.Mnemonic);
Assert.Equal("dword ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fadd, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (dword ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(32, baseRegisterMemoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
@ -76,15 +115,26 @@ public class FloatingPointInstructionTests
// Arrange
// FLD dword ptr [eax] (D9 00)
byte[] codeBuffer = new byte[] { 0xD9, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fld", instruction.Mnemonic);
Assert.Equal("dword ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fld, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (dword ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(32, baseRegisterMemoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
@ -96,15 +146,26 @@ public class FloatingPointInstructionTests
// Arrange
// FLDCW [eax] (D9 28)
byte[] codeBuffer = new byte[] { 0xD9, 0x28 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fldcw", instruction.Mnemonic);
Assert.Equal("word ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fldcw, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (word ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var memoryOperandCast = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, memoryOperandCast.BaseRegister);
Assert.Equal(16, memoryOperandCast.Size);
}
/// <summary>
@ -116,15 +177,26 @@ public class FloatingPointInstructionTests
// Arrange
// FIADD dword ptr [eax] (DA 00)
byte[] codeBuffer = new byte[] { 0xDA, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fiadd", instruction.Mnemonic);
Assert.Equal("dword ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fiadd, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (dword ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(32, baseRegisterMemoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
@ -136,15 +208,26 @@ public class FloatingPointInstructionTests
// Arrange
// FILD dword ptr [eax] (DB 00)
byte[] codeBuffer = new byte[] { 0xDB, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fild", instruction.Mnemonic);
Assert.Equal("dword ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fild, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (dword ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(32, baseRegisterMemoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
@ -156,15 +239,26 @@ public class FloatingPointInstructionTests
// Arrange
// FADD qword ptr [eax] (DC 00)
byte[] codeBuffer = new byte[] { 0xDC, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fadd", instruction.Mnemonic);
Assert.Equal("qword ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fadd, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (qword ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(64, baseRegisterMemoryOperand.Size); // Validate that it's a 64-bit memory reference
}
/// <summary>
@ -176,15 +270,31 @@ public class FloatingPointInstructionTests
// Arrange
// FADD ST(1), ST(0) (DC C1)
byte[] codeBuffer = new byte[] { 0xDC, 0xC1 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fadd", instruction.Mnemonic);
Assert.Equal("st(1), st(0)", instruction.Operands);
Assert.Equal(InstructionType.Fadd, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ST(1))
var st1Operand = instruction.StructuredOperands[0];
Assert.IsType<FPURegisterOperand>(st1Operand);
var fpuRegisterOperand1 = (FPURegisterOperand)st1Operand;
Assert.Equal(FpuRegisterIndex.ST1, fpuRegisterOperand1.RegisterIndex);
// Check the second operand (ST(0))
var st0Operand = instruction.StructuredOperands[1];
Assert.IsType<FPURegisterOperand>(st0Operand);
var fpuRegisterOperand2 = (FPURegisterOperand)st0Operand;
Assert.Equal(FpuRegisterIndex.ST0, fpuRegisterOperand2.RegisterIndex);
}
/// <summary>
@ -196,15 +306,26 @@ public class FloatingPointInstructionTests
// Arrange
// FLD qword ptr [eax] (DD 00)
byte[] codeBuffer = new byte[] { 0xDD, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fld", instruction.Mnemonic);
Assert.Equal("qword ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fld, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (qword ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(64, baseRegisterMemoryOperand.Size); // Validate that it's a 64-bit memory reference
}
/// <summary>
@ -216,15 +337,26 @@ public class FloatingPointInstructionTests
// Arrange
// FIADD word ptr [eax] (DE 00)
byte[] codeBuffer = new byte[] { 0xDE, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fiadd", instruction.Mnemonic);
Assert.Equal("word ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fiadd, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (word ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(16, baseRegisterMemoryOperand.Size); // Validate that it's a 16-bit memory reference
}
/// <summary>
@ -236,14 +368,25 @@ public class FloatingPointInstructionTests
// Arrange
// FILD word ptr [eax] (DF 00)
byte[] codeBuffer = new byte[] { 0xDF, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fild", instruction.Mnemonic);
Assert.Equal("word ptr [eax]", instruction.Operands);
Assert.Equal(InstructionType.Fild, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (word ptr [eax])
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memoryOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.A, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(16, baseRegisterMemoryOperand.Size); // Validate that it's a 16-bit memory reference
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -17,15 +18,32 @@ public class Group1InstructionTests
// ADD AL, 0x42 (80 C0 42) - ModR/M byte C0 = 11 000 000 (mod=3, reg=0, rm=0)
// mod=3 means direct register addressing, reg=0 indicates ADD operation, rm=0 is AL
byte[] codeBuffer = new byte[] { 0x80, 0xC0, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("add", instruction.Mnemonic);
Assert.Equal("al, 0x42", instruction.Operands);
Assert.Equal(InstructionType.Add, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
}
/// <summary>
@ -38,15 +56,32 @@ public class Group1InstructionTests
// ADD ECX, 0x12345678 (81 C1 78 56 34 12) - ModR/M byte C1 = 11 000 001 (mod=3, reg=0, rm=1)
// mod=3 means direct register addressing, reg=0 indicates ADD operation, rm=1 is ECX
byte[] codeBuffer = new byte[] { 0x81, 0xC1, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("add", instruction.Mnemonic);
Assert.Equal("ecx, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Add, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -59,15 +94,32 @@ public class Group1InstructionTests
// OR BL, 0x42 (80 CB 42) - ModR/M byte CB = 11 001 011 (mod=3, reg=1, rm=3)
// mod=3 means direct register addressing, reg=1 indicates OR operation, rm=3 is BL
byte[] codeBuffer = new byte[] { 0x80, 0xCB, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("or", instruction.Mnemonic);
Assert.Equal("bl, 0x42", instruction.Operands);
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (BL)
var blOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(blOperand);
var registerOperand = (RegisterOperand)blOperand;
Assert.Equal(RegisterIndex.B, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (BL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
}
/// <summary>
@ -80,15 +132,32 @@ public class Group1InstructionTests
// SUB EDX, 0x12345678 (81 EA 78 56 34 12) - ModR/M byte EA = 11 101 010 (mod=3, reg=5, rm=2)
// mod=3 means direct register addressing, reg=5 indicates SUB operation, rm=2 is EDX
byte[] codeBuffer = new byte[] { 0x81, 0xEA, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
Assert.Equal("edx, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EDX)
var edxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(edxOperand);
var registerOperand = (RegisterOperand)edxOperand;
Assert.Equal(RegisterIndex.D, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -101,15 +170,32 @@ public class Group1InstructionTests
// CMP EBX, 0x12345678 (81 FB 78 56 34 12) - ModR/M byte FB = 11 111 011 (mod=3, reg=7, rm=3)
// mod=3 means direct register addressing, reg=7 indicates CMP operation, rm=3 is EBX
byte[] codeBuffer = new byte[] { 0x81, 0xFB, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("cmp", instruction.Mnemonic);
Assert.Equal("ebx, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Cmp, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EBX)
var ebxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -122,15 +208,32 @@ public class Group1InstructionTests
// ADC ECX, 0x12345678 (81 D1 78 56 34 12) - ModR/M byte D1 = 11 010 001 (mod=3, reg=2, rm=1)
// mod=3 means direct register addressing, reg=2 indicates ADC operation, rm=1 is ECX
byte[] codeBuffer = new byte[] { 0x81, 0xD1, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("adc", instruction.Mnemonic);
Assert.Equal("ecx, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Adc, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -144,15 +247,32 @@ public class Group1InstructionTests
// mod=3 means direct register addressing, reg=2 indicates ADC operation, rm=1 is ECX
// The immediate value 0x42 is sign-extended to 32 bits
byte[] codeBuffer = new byte[] { 0x83, 0xD1, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("adc", instruction.Mnemonic);
Assert.Equal("ecx, 0x00000042", instruction.Operands);
Assert.Equal(InstructionType.Adc, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
}
/// <summary>
@ -165,15 +285,32 @@ public class Group1InstructionTests
// SBB EDX, 0x12345678 (81 DA 78 56 34 12) - ModR/M byte DA = 11 011 010 (mod=3, reg=3, rm=2)
// mod=3 means direct register addressing, reg=3 indicates SBB operation, rm=2 is EDX
byte[] codeBuffer = new byte[] { 0x81, 0xDA, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sbb", instruction.Mnemonic);
Assert.Equal("edx, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Sbb, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EDX)
var edxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(edxOperand);
var registerOperand = (RegisterOperand)edxOperand;
Assert.Equal(RegisterIndex.D, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -187,15 +324,32 @@ public class Group1InstructionTests
// mod=3 means direct register addressing, reg=3 indicates SBB operation, rm=2 is EDX
// The immediate value 0x42 is sign-extended to 32 bits
byte[] codeBuffer = new byte[] { 0x83, 0xDA, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sbb", instruction.Mnemonic);
Assert.Equal("edx, 0x00000042", instruction.Operands);
Assert.Equal(InstructionType.Sbb, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EDX)
var edxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(edxOperand);
var registerOperand = (RegisterOperand)edxOperand;
Assert.Equal(RegisterIndex.D, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
}
/// <summary>
@ -208,15 +362,32 @@ public class Group1InstructionTests
// AND EBX, 0x12345678 (81 E3 78 56 34 12) - ModR/M byte E3 = 11 100 011 (mod=3, reg=4, rm=3)
// mod=3 means direct register addressing, reg=4 indicates AND operation, rm=3 is EBX
byte[] codeBuffer = new byte[] { 0x81, 0xE3, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("and", instruction.Mnemonic);
Assert.Equal("ebx, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.And, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EBX)
var ebxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -230,15 +401,32 @@ public class Group1InstructionTests
// mod=3 means direct register addressing, reg=4 indicates AND operation, rm=3 is EBX
// The immediate value 0x42 is sign-extended to 32 bits
byte[] codeBuffer = new byte[] { 0x83, 0xE3, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("and", instruction.Mnemonic);
Assert.Equal("ebx, 0x00000042", instruction.Operands);
Assert.Equal(InstructionType.And, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EBX)
var ebxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
}
/// <summary>
@ -251,15 +439,32 @@ public class Group1InstructionTests
// XOR ESI, 0x12345678 (81 F6 78 56 34 12) - ModR/M byte F6 = 11 110 110 (mod=3, reg=6, rm=6)
// mod=3 means direct register addressing, reg=6 indicates XOR operation, rm=6 is ESI
byte[] codeBuffer = new byte[] { 0x81, 0xF6, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("xor", instruction.Mnemonic);
Assert.Equal("esi, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Xor, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ESI)
var esiOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(esiOperand);
var registerOperand = (RegisterOperand)esiOperand;
Assert.Equal(RegisterIndex.Si, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ESI)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -273,14 +478,31 @@ public class Group1InstructionTests
// mod=3 means direct register addressing, reg=6 indicates XOR operation, rm=6 is ESI
// The immediate value 0x42 is sign-extended to 32 bits
byte[] codeBuffer = new byte[] { 0x83, 0xF6, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("xor", instruction.Mnemonic);
Assert.Equal("esi, 0x42", instruction.Operands);
Assert.Equal(InstructionType.Xor, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ESI)
var esiOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(esiOperand);
var registerOperand = (RegisterOperand)esiOperand;
Assert.Equal(RegisterIndex.Si, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ESI)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,32 @@ public class Group1SignExtendedHandlerTests
// Arrange
// ADD ecx, 0x04 (83 C1 04)
byte[] codeBuffer = new byte[] { 0x83, 0xC1, 0x04 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("add", instruction.Mnemonic);
Assert.Equal("ecx, 0x00000004", instruction.Operands);
Assert.Equal(InstructionType.Add, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x04U, immediateOperand.Value);
}
/// <summary>
@ -46,11 +64,23 @@ public class Group1SignExtendedHandlerTests
Assert.True(instructions.Count >= 3, $"Expected at least 3 instructions, but got {instructions.Count}");
// First instruction should be ADD ecx, 0x04
Assert.Equal("add", instructions[0].Mnemonic);
Assert.Equal("ecx, 0x00000004", instructions[0].Operands);
var firstInstruction = instructions[0];
Assert.Equal(InstructionType.Add, firstInstruction.Type);
// Second instruction should be PUSH eax
Assert.Equal("push", instructions[1].Mnemonic);
Assert.Equal("eax", instructions[1].Operands);
// Check that we have two operands
Assert.Equal(2, firstInstruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = firstInstruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (immediate value)
var immOperand = firstInstruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x04U, immediateOperand.Value);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -17,15 +18,26 @@ public class Group3InstructionTests
// NOT EAX (F7 D0) - ModR/M byte D0 = 11 010 000 (mod=3, reg=2, rm=0)
// mod=3 means direct register addressing, reg=2 indicates NOT operation, rm=0 is EAX
byte[] codeBuffer = new byte[] { 0xF7, 0xD0 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("not", instruction.Mnemonic);
Assert.Equal("eax", instruction.Operands);
Assert.Equal(InstructionType.Not, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -38,15 +50,26 @@ public class Group3InstructionTests
// NEG ECX (F7 D9) - ModR/M byte D9 = 11 011 001 (mod=3, reg=3, rm=1)
// mod=3 means direct register addressing, reg=3 indicates NEG operation, rm=1 is ECX
byte[] codeBuffer = new byte[] { 0xF7, 0xD9 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("neg", instruction.Mnemonic);
Assert.Equal("ecx", instruction.Operands);
Assert.Equal(InstructionType.Neg, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -59,15 +82,26 @@ public class Group3InstructionTests
// MUL EDX (F7 E2) - ModR/M byte E2 = 11 100 010 (mod=3, reg=4, rm=2)
// mod=3 means direct register addressing, reg=4 indicates MUL operation, rm=2 is EDX
byte[] codeBuffer = new byte[] { 0xF7, 0xE2 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mul", instruction.Mnemonic);
Assert.Equal("edx", instruction.Operands);
Assert.Equal(InstructionType.Mul, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EDX)
var edxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(edxOperand);
var registerOperand = (RegisterOperand)edxOperand;
Assert.Equal(RegisterIndex.D, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDX)
}
/// <summary>
@ -80,15 +114,26 @@ public class Group3InstructionTests
// IMUL EBX (F7 EB) - ModR/M byte EB = 11 101 011 (mod=3, reg=5, rm=3)
// mod=3 means direct register addressing, reg=5 indicates IMUL operation, rm=3 is EBX
byte[] codeBuffer = new byte[] { 0xF7, 0xEB };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("imul", instruction.Mnemonic);
Assert.Equal("ebx", instruction.Operands);
Assert.Equal(InstructionType.IMul, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EBX)
var ebxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBX)
}
/// <summary>
@ -101,15 +146,26 @@ public class Group3InstructionTests
// DIV ESP (F7 F4) - ModR/M byte F4 = 11 110 100 (mod=3, reg=6, rm=4)
// mod=3 means direct register addressing, reg=6 indicates DIV operation, rm=4 is ESP
byte[] codeBuffer = new byte[] { 0xF7, 0xF4 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("div", instruction.Mnemonic);
Assert.Equal("esp", instruction.Operands);
Assert.Equal(InstructionType.Div, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ESP)
var espOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(espOperand);
var registerOperand = (RegisterOperand)espOperand;
Assert.Equal(RegisterIndex.Sp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ESP)
}
/// <summary>
@ -122,14 +178,25 @@ public class Group3InstructionTests
// IDIV EBP (F7 FD) - ModR/M byte FD = 11 111 101 (mod=3, reg=7, rm=5)
// mod=3 means direct register addressing, reg=7 indicates IDIV operation, rm=5 is EBP
byte[] codeBuffer = new byte[] { 0xF7, 0xFD };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("idiv", instruction.Mnemonic);
Assert.Equal("ebp", instruction.Operands);
Assert.Equal(InstructionType.IDiv, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EBP)
var ebpOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,18 @@ public class IncInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("inc", instructions[0].Mnemonic);
Assert.Equal("eax", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Inc, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -41,8 +52,18 @@ public class IncInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("inc", instructions[0].Mnemonic);
Assert.Equal("ecx", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Inc, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -60,7 +81,17 @@ public class IncInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("inc", instructions[0].Mnemonic);
Assert.Equal("edi", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Inc, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EDI)
var ediOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ediOperand);
var registerOperand = (RegisterOperand)ediOperand;
Assert.Equal(RegisterIndex.Di, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDI)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -23,13 +24,23 @@ public class InstructionDecoderTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
// The actual implementation produces "ah, 0x01" as the operands
Assert.Equal("ah, 0x01", instruction.Operands);
Assert.Equal(3, instruction.RawBytes.Length);
Assert.Equal(0xF6, instruction.RawBytes[0]);
Assert.Equal(0xC4, instruction.RawBytes[1]);
Assert.Equal(0x01, instruction.RawBytes[2]);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AH)
var ahOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ahOperand);
var ahRegisterOperand = (RegisterOperand)ahOperand;
Assert.Equal(RegisterIndex.A, ahRegisterOperand.Register);
Assert.Equal(8, ahRegisterOperand.Size); // Validate that it's an 8-bit register (AH)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x01, immediateOperand.Value);
}
/// <summary>
@ -48,11 +59,24 @@ public class InstructionDecoderTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("cl, al", instruction.Operands);
Assert.Equal(2, instruction.RawBytes.Length);
Assert.Equal(0x84, instruction.RawBytes[0]);
Assert.Equal(0xC1, instruction.RawBytes[1]);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (CL)
var clOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(clOperand);
var clRegisterOperand = (RegisterOperand)clOperand;
Assert.Equal(RegisterIndex.C, clRegisterOperand.Register);
Assert.Equal(8, clRegisterOperand.Size); // Validate that it's an 8-bit register (CL)
// Check the second operand (AL)
var alOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(alOperand);
var alRegisterOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, alRegisterOperand.Register);
Assert.Equal(8, alRegisterOperand.Size); // Validate that it's an 8-bit register (AL)
}
/// <summary>
@ -71,11 +95,24 @@ public class InstructionDecoderTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("ecx, eax", instruction.Operands);
Assert.Equal(2, instruction.RawBytes.Length);
Assert.Equal(0x85, instruction.RawBytes[0]);
Assert.Equal(0xC1, instruction.RawBytes[1]);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var ecxRegisterOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, ecxRegisterOperand.Register);
Assert.Equal(32, ecxRegisterOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -94,11 +131,24 @@ public class InstructionDecoderTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("al, 0x42", instruction.Operands);
Assert.Equal(2, instruction.RawBytes.Length);
Assert.Equal(0xA8, instruction.RawBytes[0]);
Assert.Equal(0x42, instruction.RawBytes[1]);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var alRegisterOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, alRegisterOperand.Register);
Assert.Equal(8, alRegisterOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42, immediateOperand.Value);
Assert.Equal(8, immediateOperand.Size); // Validate that it's an 8-bit immediate
}
/// <summary>
@ -117,14 +167,24 @@ public class InstructionDecoderTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(5, instruction.RawBytes.Length);
Assert.Equal(0xA9, instruction.RawBytes[0]);
Assert.Equal(0x78, instruction.RawBytes[1]);
Assert.Equal(0x56, instruction.RawBytes[2]);
Assert.Equal(0x34, instruction.RawBytes[3]);
Assert.Equal(0x12, instruction.RawBytes[4]);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var eaxRegisterOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, eaxRegisterOperand.Register);
Assert.Equal(32, eaxRegisterOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
@ -143,15 +203,24 @@ public class InstructionDecoderTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("edi, 0x12345678", instruction.Operands);
Assert.Equal(6, instruction.RawBytes.Length);
Assert.Equal(0xF7, instruction.RawBytes[0]);
Assert.Equal(0xC7, instruction.RawBytes[1]);
Assert.Equal(0x78, instruction.RawBytes[2]);
Assert.Equal(0x56, instruction.RawBytes[3]);
Assert.Equal(0x34, instruction.RawBytes[4]);
Assert.Equal(0x12, instruction.RawBytes[5]);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EDI)
var ediOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ediOperand);
var ediRegisterOperand = (RegisterOperand)ediOperand;
Assert.Equal(RegisterIndex.Di, ediRegisterOperand.Register);
Assert.Equal(32, ediRegisterOperand.Size); // Validate that it's a 32-bit register (EDI)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
@ -171,16 +240,38 @@ public class InstructionDecoderTests
// Assert - First instruction
Assert.NotNull(instruction1);
Assert.Equal("test", instruction1.Mnemonic);
Assert.Equal("ah, 0x01", instruction1.Operands);
Assert.Equal(InstructionType.Test, instruction1.Type);
// Check that we have two operands
Assert.Equal(2, instruction1.StructuredOperands.Count);
// Check the first operand (AH)
var ahOperand = instruction1.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ahOperand);
var ahRegisterOperand = (RegisterOperand)ahOperand;
Assert.Equal(RegisterIndex.A, ahRegisterOperand.Register);
Assert.Equal(8, ahRegisterOperand.Size); // Validate that it's an 8-bit register (AH)
// Check the second operand (immediate value)
var immOperand = instruction1.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x01, immediateOperand.Value);
// Act - Second instruction
var instruction2 = decoder.DecodeInstruction();
// Assert - Second instruction
Assert.NotNull(instruction2);
Assert.Equal("jz", instruction2.Mnemonic);
// The correct target address according to x86 architecture
Assert.Equal("0x00000032", instruction2.Operands);
Assert.Equal(InstructionType.Jz, instruction2.Type);
// Check that we have one operand
Assert.Single(instruction2.StructuredOperands);
// Check the operand (offset)
var offsetOperand = instruction2.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(offsetOperand);
var relativeOffset = (RelativeOffsetOperand)offsetOperand;
Assert.Equal(0x00000032UL, relativeOffset.TargetAddress);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -8,8 +9,8 @@ namespace X86DisassemblerTests.InstructionTests;
public class InstructionSequenceTests
{
/// <summary>
/// Tests that the disassembler correctly handles the sequence at address 0x10001C4B
/// </summary>
/// Tests that the disassembler correctly handles the sequence at address 0x10001C4B
/// </summary>
[Fact]
public void Disassembler_HandlesJmpSequence_Correctly()
{
@ -24,27 +25,78 @@ public class InstructionSequenceTests
Assert.True(instructions.Count >= 5, $"Expected at least 5 instructions, but got {instructions.Count}");
// First instruction: JGE LAB_10001c51 (JNL is an alternative mnemonic for JGE)
Assert.True(instructions[0].Mnemonic == "jge" || instructions[0].Mnemonic == "jnl",
$"Expected 'jge' or 'jnl', but got '{instructions[0].Mnemonic}'");
// Don't check the exact target address as it depends on the base address calculation
Assert.Equal("0x00000007", instructions[0].Operands);
Assert.True(instructions[0].Type == InstructionType.Jge,
$"Expected 'Jge', but got '{instructions[0].Type}'");
// Check the operand (immediate value for jump target)
var jgeOperand = instructions[0].StructuredOperands[0];
Assert.IsType<ImmediateOperand>(jgeOperand);
// Second instruction: ADD EBP, 0x18
Assert.Equal("add", instructions[1].Mnemonic);
Assert.Equal("ebp, 0x00000018", instructions[1].Operands);
Assert.Equal(InstructionType.Add, instructions[1].Type);
// Check the operands
Assert.Equal(2, instructions[1].StructuredOperands.Count);
// Check the first operand (EBP)
var ebpOperand = instructions[1].StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
// Check the second operand (immediate value)
var immOperand = instructions[1].StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x18U, immediateOperand.Value);
// Third instruction: JMP LAB_10001c54
Assert.Equal("jmp", instructions[2].Mnemonic);
// Don't check the exact target address as it depends on the base address calculation
Assert.Equal("0x0000000A", instructions[2].Operands);
Assert.Equal(InstructionType.Jmp, instructions[2].Type);
// Check the operand (immediate value for jump target)
var jmpOperand = instructions[2].StructuredOperands[0];
Assert.IsType<ImmediateOperand>(jmpOperand);
// Fourth instruction: ADD EBP, -0x48
Assert.Equal("add", instructions[3].Mnemonic);
Assert.Equal("ebp, 0xFFFFFFB8", instructions[3].Operands); // -0x48 sign-extended to 32-bit
Assert.Equal(InstructionType.Add, instructions[3].Type);
// Check the operands
Assert.Equal(2, instructions[3].StructuredOperands.Count);
// Check the first operand (EBP)
ebpOperand = instructions[3].StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
// Check the second operand (immediate value)
immOperand = instructions[3].StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0xFFFFFFB8U, immediateOperand.Value); // -0x48 sign-extended to 32-bit
// Fifth instruction: MOV EDX, dword ptr [ESI + 0x4]
Assert.Equal("mov", instructions[4].Mnemonic);
Assert.Equal("edx, dword ptr [esi+0x04]", instructions[4].Operands);
Assert.Equal(InstructionType.Mov, instructions[4].Type);
// Check the operands
Assert.Equal(2, instructions[4].StructuredOperands.Count);
// Check the first operand (EDX)
var edxOperand = instructions[4].StructuredOperands[0];
Assert.IsType<RegisterOperand>(edxOperand);
registerOperand = (RegisterOperand)edxOperand;
Assert.Equal(RegisterIndex.D, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDX)
// Check the second operand (memory operand)
var memOperand = instructions[4].StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Si, displacementMemoryOperand.BaseRegister);
Assert.Equal(4, displacementMemoryOperand.Displacement);
Assert.Equal(32, displacementMemoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
@ -64,31 +116,117 @@ public class InstructionSequenceTests
Assert.True(instructions.Count >= 7, $"Expected at least 7 instructions, but got {instructions.Count}");
// First instruction should be JGE with relative offset
Assert.Equal("jge", instructions[0].Mnemonic);
Assert.Equal("0x00000007", instructions[0].Operands);
Assert.Equal(InstructionType.Jge, instructions[0].Type);
// Check the operand (immediate value for jump target)
var jgeOperand = instructions[0].StructuredOperands[0];
Assert.IsType<ImmediateOperand>(jgeOperand);
// Second instruction should be ADD EBP, 0x18
Assert.Equal("add", instructions[1].Mnemonic);
Assert.Equal("ebp, 0x00000018", instructions[1].Operands);
Assert.Equal(InstructionType.Add, instructions[1].Type);
// Check the operands
Assert.Equal(2, instructions[1].StructuredOperands.Count);
// Check the first operand (EBP)
var ebpOperand = instructions[1].StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
// Check the second operand (immediate value)
var immOperand = instructions[1].StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x18U, immediateOperand.Value);
// Third instruction should be JMP
Assert.Equal("jmp", instructions[2].Mnemonic);
Assert.Equal("0x0000000A", instructions[2].Operands);
Assert.Equal(InstructionType.Jmp, instructions[2].Type);
// Check the operand (immediate value for jump target)
var jmpOperand = instructions[2].StructuredOperands[0];
Assert.IsType<ImmediateOperand>(jmpOperand);
// Fourth instruction should be ADD EBP, -0x48
Assert.Equal("add", instructions[3].Mnemonic);
Assert.Equal("ebp, 0xFFFFFFB8", instructions[3].Operands); // -0x48 sign-extended to 32-bit
Assert.Equal(InstructionType.Add, instructions[3].Type);
// Check the operands
Assert.Equal(2, instructions[3].StructuredOperands.Count);
// Check the first operand (EBP)
ebpOperand = instructions[3].StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
// Check the second operand (immediate value)
immOperand = instructions[3].StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0xFFFFFFB8U, immediateOperand.Value); // -0x48 sign-extended to 32-bit
// Fifth instruction should be MOV EDX, dword ptr [ESI+0x4]
Assert.Equal("mov", instructions[4].Mnemonic);
Assert.Equal("edx, dword ptr [esi+0x04]", instructions[4].Operands);
Assert.Equal(InstructionType.Mov, instructions[4].Type);
// Check the operands
Assert.Equal(2, instructions[4].StructuredOperands.Count);
// Check the first operand (EDX)
var edxOperand = instructions[4].StructuredOperands[0];
Assert.IsType<RegisterOperand>(edxOperand);
registerOperand = (RegisterOperand)edxOperand;
Assert.Equal(RegisterIndex.D, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDX)
// Check the second operand (memory operand)
var memOperand = instructions[4].StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Si, displacementMemoryOperand.BaseRegister);
Assert.Equal(4, displacementMemoryOperand.Displacement);
Assert.Equal(32, displacementMemoryOperand.Size); // Validate that it's a 32-bit memory reference
// Sixth instruction should be MOV AL, byte ptr [EDX]
Assert.Equal("mov", instructions[5].Mnemonic);
Assert.Equal("al, dword ptr [edx]", instructions[5].Operands);
Assert.Equal(InstructionType.Mov, instructions[5].Type);
// Check the operands
Assert.Equal(2, instructions[5].StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instructions[5].StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (memory operand)
memOperand = instructions[5].StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var baseRegisterMemoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.D, baseRegisterMemoryOperand.BaseRegister);
Assert.Equal(8, baseRegisterMemoryOperand.Size); // Validate that it's an 8-bit memory reference
// Seventh instruction should be LEA ECX, [EDX+0x18]
Assert.Equal("lea", instructions[6].Mnemonic);
Assert.Equal("ecx, [edx+0x18]", instructions[6].Operands);
Assert.Equal(InstructionType.Lea, instructions[6].Type);
// Check the operands
Assert.Equal(2, instructions[6].StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instructions[6].StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (memory operand)
memOperand = instructions[6].StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.D, displacementMemoryOperand.BaseRegister);
Assert.Equal(0x18, displacementMemoryOperand.Displacement);
Assert.Equal(32, displacementMemoryOperand.Size); // Validate that it's a 32-bit memory reference
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -23,7 +24,9 @@ public class Int3InstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("int3", instruction.Mnemonic);
Assert.Equal("", instruction.Operands);
Assert.Equal(InstructionType.Int, instruction.Type);
// Check that we have no operands
Assert.Empty(instruction.StructuredOperands);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,25 @@ public class JumpInstructionTests
// Arrange
// JMP +5 (EB 05) - Jump 5 bytes forward
byte[] codeBuffer = new byte[] { 0xEB, 0x05 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instruction = disassembler.Disassemble().First();
// Assert
Assert.NotNull(instruction);
Assert.Equal("jmp", instruction.Mnemonic);
Assert.Equal("0x00000007", instruction.Operands); // Current position (2) + offset (5) = 7
Assert.Equal(InstructionType.Jmp, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check that the operand is a relative offset operand
var operand = instruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
var relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(0x00000007UL, relativeOffsetOperand.TargetAddress); // Current position (2) + offset (5) = 7
}
/// <summary>
@ -36,15 +47,25 @@ public class JumpInstructionTests
// Arrange
// JMP +0x12345678 (E9 78 56 34 12) - Jump 0x12345678 bytes forward
byte[] codeBuffer = new byte[] { 0xE9, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instruction = disassembler.Disassemble().First();
// Assert
Assert.NotNull(instruction);
Assert.Equal("jmp", instruction.Mnemonic);
Assert.Equal("0x1234567D", instruction.Operands); // Current position (5) + offset (0x12345678) = 0x1234567D
Assert.Equal(InstructionType.Jmp, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check that the operand is a relative offset operand
var operand = instruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
var relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(0x1234567DUL, relativeOffsetOperand.TargetAddress); // Current position (5) + offset (0x12345678) = 0x1234567D
}
/// <summary>
@ -54,18 +75,27 @@ public class JumpInstructionTests
public void ConditionalJumpHandler_DecodesJzRel8_Correctly()
{
// Arrange
// JZ +10 (74 0A) - Jump 10 bytes forward if zero/equal
// Note: JZ and JE are equivalent in x86
// JZ +10 (74 0A) - Jump 10 bytes forward if zero flag is set
byte[] codeBuffer = new byte[] { 0x74, 0x0A };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instruction = disassembler.Disassemble().First();
// Assert
Assert.NotNull(instruction);
Assert.Equal("jz", instruction.Mnemonic);
Assert.Equal("0x0000000C", instruction.Operands); // Current position (2) + offset (10) = 12 (0x0C)
Assert.Equal(InstructionType.Jz, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check that the operand is a relative offset operand
var operand = instruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
var relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(0x0000000CUL, relativeOffsetOperand.TargetAddress); // Current position (2) + offset (10) = 12 (0x0C)
}
/// <summary>
@ -75,18 +105,27 @@ public class JumpInstructionTests
public void TwoByteConditionalJumpHandler_DecodesJnzRel32_Correctly()
{
// Arrange
// JNZ +0x12345678 (0F 85 78 56 34 12) - Jump 0x12345678 bytes forward if not zero/not equal
// Note: JNZ and JNE are equivalent in x86
// JNZ +0x12345678 (0F 85 78 56 34 12) - Jump 0x12345678 bytes forward if zero flag is not set
byte[] codeBuffer = new byte[] { 0x0F, 0x85, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instruction = disassembler.Disassemble().First();
// Assert
Assert.NotNull(instruction);
Assert.Equal("jnz", instruction.Mnemonic);
Assert.Equal("0x1234567E", instruction.Operands); // Current position (6) + offset (0x12345678) = 0x1234567E
Assert.Equal(InstructionType.Jnz, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check that the operand is a relative offset operand
var operand = instruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
var relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(0x1234567EUL, relativeOffsetOperand.TargetAddress); // Current position (6) + offset (0x12345678) = 0x1234567E
}
/// <summary>
@ -98,15 +137,25 @@ public class JumpInstructionTests
// Arrange
// JGE +5 (7D 05) - Jump 5 bytes forward if greater than or equal
byte[] codeBuffer = new byte[] { 0x7D, 0x05 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instruction = disassembler.Disassemble().First();
// Assert
Assert.NotNull(instruction);
Assert.Equal("jge", instruction.Mnemonic);
Assert.Equal("0x00000007", instruction.Operands); // Current position (2) + offset (5) = 7
Assert.Equal(InstructionType.Jge, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check that the operand is a relative offset operand
var operand = instruction.StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
var relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(0x00000007UL, relativeOffsetOperand.TargetAddress); // Current position (2) + offset (5) = 7
}
/// <summary>
@ -117,16 +166,27 @@ public class JumpInstructionTests
{
// Arrange
// JGE -5 (7D FB) - Jump 5 bytes backward if greater than or equal
// 0xFB is -5 in two's complement
byte[] codeBuffer = new byte[] { 0x7D, 0xFB };
var disassembler = new Disassembler(codeBuffer, 0x1000); // Set a base address for easier verification
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
Assert.Equal("jge", instructions[0].Mnemonic);
Assert.Equal("0xFFFFFFFD", instructions[0].Operands); // 0x1000 + 2 - 5 = 0xFFFFFFFD (sign-extended)
Assert.Equal(InstructionType.Jge, instructions[0].Type);
// Check that we have one operand
Assert.Single(instructions[0].StructuredOperands);
// Check that the operand is a relative offset operand
var operand = instructions[0].StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
var relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(0xFFFFFFFDUL, relativeOffsetOperand.TargetAddress); // 0 + 2 - 5 = 0xFFFFFFFD (sign-extended)
}
/// <summary>
@ -136,34 +196,88 @@ public class JumpInstructionTests
public void JgeRel8Handler_DecodesJgeRel8_InSequence_Correctly()
{
// Arrange
// This is a common pattern: JGE/JL followed by different code paths
// JGE +5 (7D 05) - Jump 5 bytes forward if greater than or equal
// ADD EBP, 0x18 (83 C5 18)
// JMP +3 (EB 03) - Jump past the next instruction
// ADD EBP, -0x48 (83 C5 B8)
// Sequence of instructions:
// 1. JGE +5 (7D 05) - Jump 5 bytes forward if greater than or equal
// 2. ADD EBP, 0x18 (83 C5 18) - Add 0x18 to EBP
// 3. JMP +3 (EB 03) - Jump 3 bytes forward
// 4. ADD EBP, -0x48 (83 C5 B8) - Add -0x48 to EBP (0xB8 is -0x48 in two's complement)
byte[] codeBuffer = new byte[] { 0x7D, 0x05, 0x83, 0xC5, 0x18, 0xEB, 0x03, 0x83, 0xC5, 0xB8 };
var disassembler = new Disassembler(codeBuffer, 0x1000);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instructions = disassembler.Disassemble();
// Assert
Assert.True(instructions.Count >= 4, $"Expected at least 4 instructions, but got {instructions.Count}");
Assert.Equal(4, instructions.Count);
// First instruction: JGE +5
Assert.Equal("jge", instructions[0].Mnemonic);
Assert.Equal("0x00000007", instructions[0].Operands); // Base address is ignored, only relative offset matters
Assert.Equal(InstructionType.Jge, instructions[0].Type);
// Check that we have one operand
Assert.Single(instructions[0].StructuredOperands);
// Check that the operand is a relative offset operand
var operand = instructions[0].StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
var relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(7UL, relativeOffsetOperand.TargetAddress); // Base address is ignored, only relative offset matters
// Second instruction: ADD EBP, 0x18
Assert.Equal("add", instructions[1].Mnemonic);
Assert.Equal("ebp, 0x00000018", instructions[1].Operands);
Assert.Equal(InstructionType.Add, instructions[1].Type);
// Check that we have two operands
Assert.Equal(2, instructions[1].StructuredOperands.Count);
// Check that the first operand is a register operand
var firstOperand = instructions[1].StructuredOperands[0];
Assert.IsType<RegisterOperand>(firstOperand);
// Check that the second operand is an immediate operand
var secondOperand = instructions[1].StructuredOperands[1];
Assert.IsType<ImmediateOperand>(secondOperand);
// Check the values of the operands
var registerOperand = (RegisterOperand)firstOperand;
var immediateOperand = (ImmediateOperand)secondOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
Assert.Equal(0x18U, immediateOperand.Value);
// Third instruction: JMP +3
Assert.Equal("jmp", instructions[2].Mnemonic);
Assert.Equal("0x0000000A", instructions[2].Operands); // Base address is ignored, only relative offset matters
Assert.Equal(InstructionType.Jmp, instructions[2].Type);
// Check that we have one operand
Assert.Single(instructions[2].StructuredOperands);
// Check that the operand is a relative offset operand
operand = instructions[2].StructuredOperands[0];
Assert.IsType<RelativeOffsetOperand>(operand);
// Check the target address
relativeOffsetOperand = (RelativeOffsetOperand)operand;
Assert.Equal(10UL, relativeOffsetOperand.TargetAddress); // Base address is ignored, only relative offset matters
// Fourth instruction: ADD EBP, -0x48 (0xB8 sign-extended to 32-bit is 0xFFFFFFB8)
Assert.Equal("add", instructions[3].Mnemonic);
Assert.Equal("ebp, 0xFFFFFFB8", instructions[3].Operands);
Assert.Equal(InstructionType.Add, instructions[3].Type);
// Check that we have two operands
Assert.Equal(2, instructions[3].StructuredOperands.Count);
// Check that the first operand is a register operand
firstOperand = instructions[3].StructuredOperands[0];
Assert.IsType<RegisterOperand>(firstOperand);
// Check that the second operand is an immediate operand
secondOperand = instructions[3].StructuredOperands[1];
Assert.IsType<ImmediateOperand>(secondOperand);
// Check the values of the operands
registerOperand = (RegisterOperand)firstOperand;
immediateOperand = (ImmediateOperand)secondOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
Assert.Equal(0xFFFFFFB8L, immediateOperand.Value);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,24 @@ public class LeaInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("lea", instructions[0].Mnemonic);
Assert.Equal("eax, [eax]", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Lea, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.A, memoryOperand.BaseRegister);
}
/// <summary>
@ -41,18 +58,35 @@ public class LeaInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("lea", instructions[0].Mnemonic);
Assert.Equal("edi, [esi-0x04]", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Lea, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EDI)
var ediOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ediOperand);
var registerOperand = (RegisterOperand)ediOperand;
Assert.Equal(RegisterIndex.Di, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDI)
// Check the second operand (memory operand with displacement)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Si, displacementMemoryOperand.BaseRegister);
Assert.Equal(-4, displacementMemoryOperand.Displacement);
}
/// <summary>
/// Tests the LEA r32, m instruction (0x8D) with SIB byte
/// </summary>
[Fact]
public void TestLeaR32M_WithSIB()
public void TestLeaR32M_WithSib()
{
// Arrange
byte[] code = { 0x8D, 0x04, 0x11 }; // LEA EAX, [ECX+EDX]
byte[] code = { 0x8D, 0x04, 0x8D, 0x00, 0x00, 0x00, 0x00 }; // LEA EAX, [ECX*4]
// Act
Disassembler disassembler = new Disassembler(code, 0x1000);
@ -60,8 +94,26 @@ public class LeaInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("lea", instructions[0].Mnemonic);
Assert.Equal("eax, [ecx+edx]", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Lea, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (scaled index memory operand)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<ScaledIndexMemoryOperand>(memOperand);
var scaledIndexMemoryOperand = (ScaledIndexMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.C, scaledIndexMemoryOperand.IndexRegister);
Assert.Equal(4, scaledIndexMemoryOperand.Scale);
Assert.Equal(0, scaledIndexMemoryOperand.Displacement);
}
/// <summary>
@ -71,7 +123,7 @@ public class LeaInstructionTests
public void TestLeaR32M_Complex()
{
// Arrange
byte[] code = { 0x8D, 0x44, 0x8A, 0x10 }; // LEA EAX, [EDX + ECX*4 + 0x10]
byte[] code = { 0x8D, 0x84, 0x88, 0x78, 0x56, 0x34, 0x12 }; // LEA EAX, [EAX+ECX*4+0x12345678]
// Act
Disassembler disassembler = new Disassembler(code, 0x1000);
@ -79,7 +131,26 @@ public class LeaInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("lea", instructions[0].Mnemonic);
Assert.Equal("eax, [edx+ecx*4+0x10]", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Lea, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (complex memory operand)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<ScaledIndexMemoryOperand>(memOperand);
var scaledIndexMemoryOperand = (ScaledIndexMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.A, scaledIndexMemoryOperand.BaseRegister);
Assert.Equal(RegisterIndex.C, scaledIndexMemoryOperand.IndexRegister);
Assert.Equal(4, scaledIndexMemoryOperand.Scale);
Assert.Equal(0x12345678, scaledIndexMemoryOperand.Displacement);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,25 @@ public class MovRm32Imm32Tests
// Assert
Assert.Single(instructions);
Assert.Equal("mov", instructions[0].Mnemonic);
Assert.Equal("eax, 0x12345678", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
@ -41,8 +59,25 @@ public class MovRm32Imm32Tests
// Assert
Assert.Single(instructions);
Assert.Equal("mov", instructions[0].Mnemonic);
Assert.Equal("dword ptr [eax], 0x12345678", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.A, memoryOperand.BaseRegister);
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
@ -61,8 +96,26 @@ public class MovRm32Imm32Tests
// Assert
Assert.Single(instructions);
Assert.Equal("mov", instructions[0].Mnemonic);
Assert.Equal("dword ptr [esp+0x10], 0x00000000", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand with SIB)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Sp, displacementMemoryOperand.BaseRegister);
Assert.Equal(32, displacementMemoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0x10, displacementMemoryOperand.Displacement);
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000000U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
@ -81,8 +134,28 @@ public class MovRm32Imm32Tests
// Assert
Assert.Single(instructions);
Assert.Equal("mov", instructions[0].Mnemonic);
Assert.Equal("dword ptr [eax+ecx*4+0x12345678], 0xAABBCCDD", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand with SIB)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<ScaledIndexMemoryOperand>(memOperand);
var sibMemoryOperand = (ScaledIndexMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.A, sibMemoryOperand.BaseRegister);
Assert.Equal(RegisterIndex.C, sibMemoryOperand.IndexRegister);
Assert.Equal(4, sibMemoryOperand.Scale);
Assert.Equal(32, sibMemoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0x12345678, sibMemoryOperand.Displacement);
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0xAABBCCDDU, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
@ -107,14 +180,50 @@ public class MovRm32Imm32Tests
Assert.Equal(2, instructions.Count);
// First instruction
Assert.Equal("mov", instructions[0].Mnemonic);
Assert.Equal("dword ptr [esp+0x10], 0x00000000", instructions[0].Operands);
var firstInstruction = instructions[0];
Assert.Equal(InstructionType.Mov, firstInstruction.Type);
// Check that we have two operands
Assert.Equal(2, firstInstruction.StructuredOperands.Count);
// Check the first operand (memory operand with SIB)
var memOperand = firstInstruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Sp, displacementMemoryOperand.BaseRegister);
Assert.Equal(32, displacementMemoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0x10, displacementMemoryOperand.Displacement);
// Check the second operand (immediate value)
var immOperand = firstInstruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000000U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
// Second instruction
Assert.Equal("mov", instructions[1].Mnemonic);
Assert.Equal("dword ptr [esp+0x14], 0x00000000", instructions[1].Operands);
var secondInstruction = instructions[1];
Assert.Equal(InstructionType.Mov, secondInstruction.Type);
// Check that we have two operands
Assert.Equal(2, secondInstruction.StructuredOperands.Count);
// Check the first operand (memory operand with SIB)
memOperand = secondInstruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Sp, displacementMemoryOperand.BaseRegister);
Assert.Equal(32, displacementMemoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0x14, displacementMemoryOperand.Displacement);
// Check the second operand (immediate value)
immOperand = secondInstruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000000U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
/// Tests the MOV m32, imm32 instruction (0xC7) with instruction boundary detection
/// </summary>
@ -138,11 +247,47 @@ public class MovRm32Imm32Tests
Assert.Equal(2, instructions.Count);
// First instruction
Assert.Equal("mov", instructions[0].Mnemonic);
Assert.Equal("dword ptr [esp+0x10], 0x00000000", instructions[0].Operands);
var firstInstruction = instructions[0];
Assert.Equal(InstructionType.Mov, firstInstruction.Type);
// Check that we have two operands
Assert.Equal(2, firstInstruction.StructuredOperands.Count);
// Check the first operand (memory operand with SIB)
var memOperand = firstInstruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Sp, displacementMemoryOperand.BaseRegister);
Assert.Equal(32, displacementMemoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0x10, displacementMemoryOperand.Displacement);
// Check the second operand (immediate value)
var immOperand = firstInstruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000000U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
// Second instruction
Assert.Equal("mov", instructions[1].Mnemonic);
Assert.Equal("dword ptr [esp+0x14], 0x00000000", instructions[1].Operands);
var secondInstruction = instructions[1];
Assert.Equal(InstructionType.Mov, secondInstruction.Type);
// Check that we have two operands
Assert.Equal(2, secondInstruction.StructuredOperands.Count);
// Check the first operand (memory operand with SIB)
memOperand = secondInstruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
displacementMemoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Sp, displacementMemoryOperand.BaseRegister);
Assert.Equal(32, displacementMemoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0x14, displacementMemoryOperand.Displacement);
// Check the second operand (immediate value)
immOperand = secondInstruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000000U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,25 @@ public class MovRm8Imm8Tests
// Assert
Assert.Single(instructions);
Assert.Equal("mov", instructions[0].Mnemonic);
Assert.Equal("al, 0x42", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
Assert.Equal(8, immediateOperand.Size); // Validate that it's an 8-bit immediate
}
/// <summary>
@ -41,7 +59,24 @@ public class MovRm8Imm8Tests
// Assert
Assert.Single(instructions);
Assert.Equal("mov", instructions[0].Mnemonic);
Assert.Equal("byte ptr [ecx], 0x01", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Mov, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.C, memoryOperand.BaseRegister);
Assert.Equal(8, memoryOperand.Size); // Validate that it's an 8-bit memory reference
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x01U, immediateOperand.Value);
Assert.Equal(8, immediateOperand.Size); // Validate that it's an 8-bit immediate
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -22,8 +23,25 @@ public class OrInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("or", instructions[0].Mnemonic);
Assert.Equal("cl, al", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (CL)
var clOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(clOperand);
var registerOperand1 = (RegisterOperand)clOperand;
Assert.Equal(RegisterIndex.C, registerOperand1.Register);
Assert.Equal(8, registerOperand1.Size); // Validate that it's an 8-bit register (CL)
// Check the second operand (AL)
var alOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand2 = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand2.Register);
Assert.Equal(8, registerOperand2.Size); // Validate that it's an 8-bit register (AL)
}
/// <summary>
@ -41,8 +59,25 @@ public class OrInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("or", instructions[0].Mnemonic);
Assert.Equal("al, byte ptr [eax]", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (memory operand)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(8, memoryOperand.Size); // Validate that it's an 8-bit memory reference
Assert.Equal(RegisterIndex.A, memoryOperand.BaseRegister);
}
/// <summary>
@ -60,8 +95,25 @@ public class OrInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("or", instructions[0].Mnemonic);
Assert.Equal("ecx, eax", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand1 = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand2 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -79,8 +131,25 @@ public class OrInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("or", instructions[0].Mnemonic);
Assert.Equal("eax, dword ptr [eax]", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(RegisterIndex.A, memoryOperand.BaseRegister);
}
/// <summary>
@ -98,8 +167,24 @@ public class OrInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("or", instructions[0].Mnemonic);
Assert.Equal("al, 0x42", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42, immediateOperand.Value);
}
/// <summary>
@ -117,8 +202,24 @@ public class OrInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("or", instructions[0].Mnemonic);
Assert.Equal("eax, 0x12345678", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678, immediateOperand.Value);
}
/// <summary>
@ -136,8 +237,24 @@ public class OrInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("or", instructions[0].Mnemonic);
Assert.Equal("eax, 0x12345678", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678, immediateOperand.Value);
}
/// <summary>
@ -155,7 +272,23 @@ public class OrInstructionTests
// Assert
Assert.Single(instructions);
Assert.Equal("or", instructions[0].Mnemonic);
Assert.Equal("eax, 0x00000042", instructions[0].Operands);
var instruction = instructions[0];
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000042, immediateOperand.Value);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,36 @@ public class OrRm8R8HandlerTests
// Arrange
// OR [ebx+ecx*4+0x41], al (08 44 8B 41)
byte[] codeBuffer = new byte[] { 0x08, 0x44, 0x8B, 0x41 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("or", instruction.Mnemonic);
Assert.Equal("byte ptr [ebx+ecx*4+0x41], al", instruction.Operands);
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand with SIB)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<ScaledIndexMemoryOperand>(memOperand);
var scaledIndexMemoryOperand = (ScaledIndexMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.B, scaledIndexMemoryOperand.BaseRegister);
Assert.Equal(RegisterIndex.C, scaledIndexMemoryOperand.IndexRegister);
Assert.Equal(4, scaledIndexMemoryOperand.Scale);
Assert.Equal(0x41, scaledIndexMemoryOperand.Displacement);
Assert.Equal(8, scaledIndexMemoryOperand.Size); // Validate that it's an 8-bit memory reference
// Check the second operand (AL)
var alOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
}
/// <summary>
@ -36,14 +58,32 @@ public class OrRm8R8HandlerTests
// Arrange
// OR bl, ch (08 EB)
byte[] codeBuffer = new byte[] { 0x08, 0xEB };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("or", instruction.Mnemonic);
Assert.Equal("bl, ch", instruction.Operands);
Assert.Equal(InstructionType.Or, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (BL)
var blOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(blOperand);
var registerOperand1 = (RegisterOperand)blOperand;
Assert.Equal(RegisterIndex.B, registerOperand1.Register);
Assert.Equal(8, registerOperand1.Size); // Validate that it's an 8-bit register (BL)
// Check the second operand (CH)
var chOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(chOperand);
var registerOperand2 = (RegisterOperand)chOperand;
Assert.Equal(RegisterIndex.C, registerOperand2.Register);
Assert.Equal(8, registerOperand2.Size); // Validate that it's an 8-bit register (CH)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,26 @@ public class PushPopInstructionTests
// Arrange
// PUSH EAX (50) - Push EAX onto the stack
byte[] codeBuffer = new byte[] { 0x50 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("push", instruction.Mnemonic);
Assert.Equal("eax", instruction.Operands);
Assert.Equal(InstructionType.Push, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -36,15 +48,26 @@ public class PushPopInstructionTests
// Arrange
// PUSH EBP (55) - Push EBP onto the stack
byte[] codeBuffer = new byte[] { 0x55 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("push", instruction.Mnemonic);
Assert.Equal("ebp", instruction.Operands);
Assert.Equal(InstructionType.Push, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EBP)
var ebpOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
}
/// <summary>
@ -54,17 +77,27 @@ public class PushPopInstructionTests
public void PushImm8Handler_DecodesPushImm8_Correctly()
{
// Arrange
// PUSH 0x42 (6A 42) - Push 8-bit immediate value onto the stack
// PUSH 0x42 (6A 42) - Push immediate byte value 0x42 onto the stack
byte[] codeBuffer = new byte[] { 0x6A, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("push", instruction.Mnemonic);
Assert.Equal("0x42", instruction.Operands);
Assert.Equal(InstructionType.Push, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (immediate value)
var immOperand = instruction.StructuredOperands[0];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
}
/// <summary>
@ -74,17 +107,27 @@ public class PushPopInstructionTests
public void PushImm32Handler_DecodesPushImm32_Correctly()
{
// Arrange
// PUSH 0x12345678 (68 78 56 34 12) - Push 32-bit immediate value onto the stack
// PUSH 0x12345678 (68 78 56 34 12) - Push immediate dword value 0x12345678 onto the stack
byte[] codeBuffer = new byte[] { 0x68, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("push", instruction.Mnemonic);
Assert.Equal("0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Push, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (immediate value)
var immOperand = instruction.StructuredOperands[0];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -94,17 +137,28 @@ public class PushPopInstructionTests
public void PopRegHandler_DecodesPopReg_Correctly()
{
// Arrange
// POP EAX (58) - Pop value from stack into EAX
// POP EAX (58) - Pop a value from the stack into EAX
byte[] codeBuffer = new byte[] { 0x58 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("pop", instruction.Mnemonic);
Assert.Equal("eax", instruction.Operands);
Assert.Equal(InstructionType.Pop, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -114,17 +168,28 @@ public class PushPopInstructionTests
public void PopRegHandler_DecodesPopEbp_Correctly()
{
// Arrange
// POP EBP (5D) - Pop value from stack into EBP
// POP EBP (5D) - Pop a value from the stack into EBP
byte[] codeBuffer = new byte[] { 0x5D };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("pop", instruction.Mnemonic);
Assert.Equal("ebp", instruction.Operands);
Assert.Equal(InstructionType.Pop, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (EBP)
var ebpOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
}
/// <summary>
@ -146,12 +211,40 @@ public class PushPopInstructionTests
Assert.Equal(2, instructions.Count);
// First instruction: PUSH EBP
Assert.Equal("push", instructions[0].Mnemonic);
Assert.Equal("ebp", instructions[0].Operands);
var pushInstruction = instructions[0];
Assert.NotNull(pushInstruction);
Assert.Equal(InstructionType.Push, pushInstruction.Type);
// Check that we have one operand
Assert.Single(pushInstruction.StructuredOperands);
// Check the operand (EBP)
var ebpOperand = pushInstruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
// Second instruction: MOV EBP, ESP
Assert.Equal("mov", instructions[1].Mnemonic);
Assert.Equal("ebp, esp", instructions[1].Operands);
var movInstruction = instructions[1];
Assert.NotNull(movInstruction);
Assert.Equal(InstructionType.Move, movInstruction.Type);
// Check that we have two operands
Assert.Equal(2, movInstruction.StructuredOperands.Count);
// Check the operands (EBP and ESP)
var ebpDestOperand = movInstruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpDestOperand);
var ebpDestRegisterOperand = (RegisterOperand)ebpDestOperand;
Assert.Equal(RegisterIndex.Bp, ebpDestRegisterOperand.Register);
Assert.Equal(32, ebpDestRegisterOperand.Size); // Validate that it's a 32-bit register (EBP)
var espSrcOperand = movInstruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(espSrcOperand);
var espSrcRegisterOperand = (RegisterOperand)espSrcOperand;
Assert.Equal(RegisterIndex.Sp, espSrcRegisterOperand.Register);
Assert.Equal(32, espSrcRegisterOperand.Size); // Validate that it's a 32-bit register (ESP)
}
/// <summary>
@ -173,11 +266,26 @@ public class PushPopInstructionTests
Assert.Equal(2, instructions.Count);
// First instruction: POP EBP
Assert.Equal("pop", instructions[0].Mnemonic);
Assert.Equal("ebp", instructions[0].Operands);
var popInstruction = instructions[0];
Assert.NotNull(popInstruction);
Assert.Equal(InstructionType.Pop, popInstruction.Type);
// Check that we have one operand
Assert.Single(popInstruction.StructuredOperands);
// Check the operand (EBP)
var ebpOperand = popInstruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBP)
// Second instruction: RET
Assert.Equal("ret", instructions[1].Mnemonic);
Assert.Equal("", instructions[1].Operands);
var retInstruction = instructions[1];
Assert.NotNull(retInstruction);
Assert.Equal(InstructionType.Ret, retInstruction.Type);
// Check that we have no operands
Assert.Empty(retInstruction.StructuredOperands);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,19 @@ public class ReturnInstructionTests
// Arrange
// RET (C3) - Return from procedure
byte[] codeBuffer = new byte[] { 0xC3 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("ret", instruction.Mnemonic);
Assert.Equal("", instruction.Operands);
Assert.Equal(InstructionType.Ret, instruction.Type);
// Check that we have no operands
Assert.Empty(instruction.StructuredOperands);
}
/// <summary>
@ -36,14 +41,25 @@ public class ReturnInstructionTests
// Arrange
// RET 0x1234 (C2 34 12) - Return from procedure and pop 0x1234 bytes
byte[] codeBuffer = new byte[] { 0xC2, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("ret", instruction.Mnemonic);
Assert.Equal("0x1234", instruction.Operands);
Assert.Equal(InstructionType.Ret, instruction.Type);
// Check that we have one operand
Assert.Single(instruction.StructuredOperands);
// Check the operand (immediate value)
var immOperand = instruction.StructuredOperands[0];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x1234U, immediateOperand.Value);
Assert.Equal(16, immediateOperand.Size); // Validate that it's a 16-bit immediate
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -17,15 +18,33 @@ public class SbbInstructionTests
// SBB EAX, 0x12345678 (81 D8 78 56 34 12) - ModR/M byte D8 = 11 011 000 (mod=3, reg=3, rm=0)
// mod=3 means direct register addressing, reg=3 is the SBB opcode extension, rm=0 is EAX
byte[] codeBuffer = new byte[] { 0x81, 0xD8, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sbb", instruction.Mnemonic);
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Sbb, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
@ -38,14 +57,32 @@ public class SbbInstructionTests
// SBB EAX, 0x42 (83 D8 42) - ModR/M byte D8 = 11 011 000 (mod=3, reg=3, rm=0)
// mod=3 means direct register addressing, reg=3 is the SBB opcode extension, rm=0 is EAX
byte[] codeBuffer = new byte[] { 0x83, 0xD8, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sbb", instruction.Mnemonic);
Assert.Equal("eax, 0x00000042", instruction.Operands);
Assert.Equal(InstructionType.Sbb, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x00000042U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,34 @@ public class SegmentOverrideTests
// Arrange
// CS segment override prefix (0x2E) followed by MOV EAX, [0] (8B 05 00 00 00 00)
byte[] codeBuffer = new byte[] { 0x2E, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, dword ptr cs:[0x00000000]", instruction.Operands);
Assert.Equal(InstructionType.Move, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand with CS segment override)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DirectMemoryOperand>(memOperand);
var memoryOperand = (DirectMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0, memoryOperand.Address);
Assert.Equal("cs", memoryOperand.SegmentOverride);
}
/// <summary>
@ -36,15 +56,34 @@ public class SegmentOverrideTests
// Arrange
// DS segment override prefix (0x3E) followed by MOV EAX, [0] (8B 05 00 00 00 00)
byte[] codeBuffer = new byte[] { 0x3E, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, dword ptr ds:[0x00000000]", instruction.Operands);
Assert.Equal(InstructionType.Move, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand with DS segment override)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DirectMemoryOperand>(memOperand);
var memoryOperand = (DirectMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0, memoryOperand.Address);
Assert.Equal("ds", memoryOperand.SegmentOverride);
}
/// <summary>
@ -56,15 +95,34 @@ public class SegmentOverrideTests
// Arrange
// ES segment override prefix (0x26) followed by MOV EAX, [0] (8B 05 00 00 00 00)
byte[] codeBuffer = new byte[] { 0x26, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, dword ptr es:[0x00000000]", instruction.Operands);
Assert.Equal(InstructionType.Move, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand with ES segment override)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DirectMemoryOperand>(memOperand);
var memoryOperand = (DirectMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0, memoryOperand.Address);
Assert.Equal("es", memoryOperand.SegmentOverride);
}
/// <summary>
@ -76,15 +134,34 @@ public class SegmentOverrideTests
// Arrange
// FS segment override prefix (0x64) followed by MOV [0], ESP (89 25 00 00 00 00)
byte[] codeBuffer = new byte[] { 0x64, 0x89, 0x25, 0x00, 0x00, 0x00, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("dword ptr fs:[0x00000000], esp", instruction.Operands);
Assert.Equal(InstructionType.Move, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand with FS segment override)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<DirectMemoryOperand>(memOperand);
var memoryOperand = (DirectMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0, memoryOperand.Address);
Assert.Equal("fs", memoryOperand.SegmentOverride);
// Check the second operand (ESP)
var espOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(espOperand);
var registerOperand = (RegisterOperand)espOperand;
Assert.Equal(RegisterIndex.Sp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ESP)
}
/// <summary>
@ -96,15 +173,34 @@ public class SegmentOverrideTests
// Arrange
// GS segment override prefix (0x65) followed by MOV EAX, [0] (8B 05 00 00 00 00)
byte[] codeBuffer = new byte[] { 0x65, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, dword ptr gs:[0x00000000]", instruction.Operands);
Assert.Equal(InstructionType.Move, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand with GS segment override)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DirectMemoryOperand>(memOperand);
var memoryOperand = (DirectMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0, memoryOperand.Address);
Assert.Equal("gs", memoryOperand.SegmentOverride);
}
/// <summary>
@ -116,15 +212,35 @@ public class SegmentOverrideTests
// Arrange
// SS segment override prefix (0x36) followed by MOV EAX, [EBP-4] (8B 45 FC)
byte[] codeBuffer = new byte[] { 0x36, 0x8B, 0x45, 0xFC };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, dword ptr ss:[ebp-0x04]", instruction.Operands);
Assert.Equal(InstructionType.Move, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand with SS segment override)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memOperand);
var memoryOperand = (DisplacementMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal("ss", memoryOperand.SegmentOverride);
Assert.Equal(RegisterIndex.Bp, memoryOperand.BaseRegister);
Assert.Equal(-4, memoryOperand.Displacement);
}
/// <summary>
@ -136,15 +252,16 @@ public class SegmentOverrideTests
// Arrange
// Just the FS segment override prefix (0x64)
byte[] codeBuffer = new byte[] { 0x64 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("fs", instruction.Mnemonic);
Assert.Equal("", instruction.Operands);
Assert.Equal(InstructionType.Rep, instruction.Type);
}
/// <summary>
@ -156,15 +273,37 @@ public class SegmentOverrideTests
// Arrange
// FS segment override prefix (0x64) followed by MOV EAX, [EBX+ECX*4+0x10] (8B 84 8B 10 00 00 00)
byte[] codeBuffer = new byte[] { 0x64, 0x8B, 0x84, 0x8B, 0x10, 0x00, 0x00, 0x00 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("mov", instruction.Mnemonic);
Assert.Equal("eax, dword ptr fs:[ebx+ecx*4+0x10]", instruction.Operands);
Assert.Equal(InstructionType.Move, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand with FS segment override)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<ScaledIndexMemoryOperand>(memOperand);
var memoryOperand = (ScaledIndexMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal("fs", memoryOperand.SegmentOverride);
Assert.Equal(RegisterIndex.B, memoryOperand.BaseRegister);
Assert.Equal(RegisterIndex.C, memoryOperand.IndexRegister);
Assert.Equal(4, memoryOperand.Scale);
Assert.Equal(0x10, memoryOperand.Displacement);
}
/// <summary>
@ -176,16 +315,34 @@ public class SegmentOverrideTests
// Arrange
// ES segment override prefix (0x26) followed by LODS DWORD PTR DS:[ESI] (AD)
byte[] codeBuffer = new byte[] { 0x26, 0xAD };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("lods", instruction.Mnemonic);
// The string instruction uses DS:ESI by default, but with ES override it becomes ES:ESI
Assert.Equal("eax, dword ptr es:[esi]", instruction.Operands);
Assert.Equal(InstructionType.LodsD, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand with ES segment override)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<DirectMemoryOperand>(memOperand);
var memoryOperand = (DirectMemoryOperand)memOperand;
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
Assert.Equal(0, memoryOperand.Address);
Assert.Equal("es", memoryOperand.SegmentOverride);
}
/// <summary>
@ -197,14 +354,34 @@ public class SegmentOverrideTests
// Arrange
// REP prefix (F3) followed by FS segment override prefix (0x64) followed by MOVS (A4)
byte[] codeBuffer = new byte[] { 0xF3, 0x64, 0xA4 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("rep movs", instruction.Mnemonic);
Assert.Equal("byte ptr fs:[edi], byte ptr fs:[esi]", instruction.Operands);
Assert.Equal(InstructionType.RepMovsB, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand with FS segment override)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(8, memoryOperand.Size); // Validate that it's a byte memory reference
Assert.Equal("fs", memoryOperand.SegmentOverride);
Assert.Equal(RegisterIndex.Di, memoryOperand.BaseRegister);
// Check the second operand (memory operand with FS segment override)
var memOperand2 = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand2);
var memoryOperand2 = (BaseRegisterMemoryOperand)memOperand2;
Assert.Equal(8, memoryOperand2.Size); // Validate that it's a byte memory reference
Assert.Equal("fs", memoryOperand2.SegmentOverride);
Assert.Equal(RegisterIndex.Si, memoryOperand2.BaseRegister);
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,33 @@ public class StringInstructionHandlerTests
// Arrange
// REP MOVS (F3 A5)
byte[] codeBuffer = new byte[] { 0xF3, 0xA5 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("rep movs", instruction.Mnemonic);
Assert.Equal("dword ptr [edi], dword ptr [esi]", instruction.Operands);
Assert.Equal(InstructionType.RepMovsD, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (destination memory operand)
var destOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(destOperand);
var destMemoryOperand = (BaseRegisterMemoryOperand)destOperand;
Assert.Equal(RegisterIndex.Di, destMemoryOperand.BaseRegister);
Assert.Equal(32, destMemoryOperand.Size); // Validate that it's a 32-bit memory reference
// Check the second operand (source memory operand)
var srcOperand = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(srcOperand);
var srcMemoryOperand = (BaseRegisterMemoryOperand)srcOperand;
Assert.Equal(RegisterIndex.Si, srcMemoryOperand.BaseRegister);
Assert.Equal(32, srcMemoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
@ -36,15 +55,33 @@ public class StringInstructionHandlerTests
// Arrange
// REPNE SCAS (F2 AF)
byte[] codeBuffer = new byte[] { 0xF2, 0xAF };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("repne scas", instruction.Mnemonic);
Assert.Equal("eax, dword ptr [edi]", instruction.Operands);
Assert.Equal(InstructionType.RepNE, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Di, memoryOperand.BaseRegister);
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
@ -56,19 +93,37 @@ public class StringInstructionHandlerTests
// Arrange
// MOVS (A5)
byte[] codeBuffer = new byte[] { 0xA5 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("movs", instruction.Mnemonic);
Assert.Equal("dword ptr [edi], dword ptr [esi]", instruction.Operands);
Assert.Equal(InstructionType.MovsD, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (destination memory operand)
var destOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(destOperand);
var destMemoryOperand = (BaseRegisterMemoryOperand)destOperand;
Assert.Equal(RegisterIndex.Di, destMemoryOperand.BaseRegister);
Assert.Equal(32, destMemoryOperand.Size); // Validate that it's a 32-bit memory reference
// Check the second operand (source memory operand)
var srcOperand = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(srcOperand);
var srcMemoryOperand = (BaseRegisterMemoryOperand)srcOperand;
Assert.Equal(RegisterIndex.Si, srcMemoryOperand.BaseRegister);
Assert.Equal(32, srcMemoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
/// Tests the StringInstructionHandler for decoding STOS instruction without prefix
/// Tests the StringInstructionHandler for decoding STOSB instruction
/// </summary>
[Fact]
public void StringInstructionHandler_DecodesStosb_Correctly()
@ -76,19 +131,37 @@ public class StringInstructionHandlerTests
// Arrange
// STOSB (AA)
byte[] codeBuffer = new byte[] { 0xAA };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("stos", instruction.Mnemonic);
Assert.Equal("byte ptr [edi], al", instruction.Operands);
Assert.Equal(InstructionType.StosB, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memOperand = instruction.StructuredOperands[0];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Di, memoryOperand.BaseRegister);
Assert.Equal(8, memoryOperand.Size); // Validate that it's an 8-bit memory reference
// Check the second operand (AL)
var alOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
}
/// <summary>
/// Tests the StringInstructionHandler for decoding LODS instruction without prefix
/// Tests the StringInstructionHandler for decoding LODSD instruction
/// </summary>
[Fact]
public void StringInstructionHandler_DecodesLodsd_Correctly()
@ -96,19 +169,37 @@ public class StringInstructionHandlerTests
// Arrange
// LODSD (AD)
byte[] codeBuffer = new byte[] { 0xAD };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("lods", instruction.Mnemonic);
Assert.Equal("eax, dword ptr [esi]", instruction.Operands);
Assert.Equal(InstructionType.LodsD, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (memory operand)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Si, memoryOperand.BaseRegister);
Assert.Equal(32, memoryOperand.Size); // Validate that it's a 32-bit memory reference
}
/// <summary>
/// Tests the StringInstructionHandler for decoding SCAS instruction without prefix
/// Tests the StringInstructionHandler for decoding SCASB instruction
/// </summary>
[Fact]
public void StringInstructionHandler_DecodesScasb_Correctly()
@ -116,14 +207,32 @@ public class StringInstructionHandlerTests
// Arrange
// SCASB (AE)
byte[] codeBuffer = new byte[] { 0xAE };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("scas", instruction.Mnemonic);
Assert.Equal("al, byte ptr [edi]", instruction.Operands);
Assert.Equal(InstructionType.ScasB, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (memory operand)
var memOperand = instruction.StructuredOperands[1];
Assert.IsType<BaseRegisterMemoryOperand>(memOperand);
var memoryOperand = (BaseRegisterMemoryOperand)memOperand;
Assert.Equal(RegisterIndex.Di, memoryOperand.BaseRegister);
Assert.Equal(8, memoryOperand.Size); // Validate that it's an 8-bit memory reference
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -16,15 +17,32 @@ public class SubInstructionTests
// Arrange
// SUB EAX, 0x12345678 (81 E8 78 56 34 12) - Subtract 0x12345678 from EAX
byte[] codeBuffer = new byte[] { 0x81, 0xE8, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -36,16 +54,33 @@ public class SubInstructionTests
// Arrange
// SUB [EBX+0x10], 0x12345678 (81 6B 10 78 56 34 12) - Subtract 0x12345678 from memory at [EBX+0x10]
byte[] codeBuffer = new byte[] { 0x81, 0x6B, 0x10, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
// The actual output from the disassembler for this instruction
Assert.Equal("dword ptr [ebx+0x10], 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand);
var memory = (DisplacementMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.B, memory.BaseRegister); // Base register is EBX
Assert.Equal(0x10, memory.Displacement); // Displacement is 0x10
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
}
/// <summary>
@ -57,15 +92,32 @@ public class SubInstructionTests
// Arrange
// SUB EAX, 0x42 (83 E8 42) - Subtract sign-extended 0x42 from EAX
byte[] codeBuffer = new byte[] { 0x83, 0xE8, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
Assert.Equal("eax, 0x42", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42, immediateOperand.Value);
}
/// <summary>
@ -77,16 +129,32 @@ public class SubInstructionTests
// Arrange
// SUB EAX, -0x10 (83 E8 F0) - Subtract sign-extended -0x10 from EAX
byte[] codeBuffer = new byte[] { 0x83, 0xE8, 0xF0 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
// F0 is -16 in two's complement, should be sign-extended to 0xFFFFFFF0
Assert.Equal("eax, 0xFFFFFFF0", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0xFFFFFFF0U, immediateOperand.Value);
}
/// <summary>
@ -98,16 +166,33 @@ public class SubInstructionTests
// Arrange
// SUB [EBX+0x10], 0x42 (83 6B 10 42) - Subtract sign-extended 0x42 from memory at [EBX+0x10]
byte[] codeBuffer = new byte[] { 0x83, 0x6B, 0x10, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
// The actual output from the disassembler for this instruction
Assert.Equal("dword ptr [ebx+0x10], 0x42", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand);
var memory = (DisplacementMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.B, memory.BaseRegister); // Base register is EBX
Assert.Equal(0x10, memory.Displacement); // Displacement is 0x10
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42, immediateOperand.Value);
}
/// <summary>
@ -128,8 +213,25 @@ public class SubInstructionTests
Assert.Single(instructions);
// Instruction: SUB ESP, 0x10
Assert.Equal("sub", instructions[0].Mnemonic);
Assert.Equal("esp, 0x10", instructions[0].Operands);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ESP)
var espOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(espOperand);
var registerOperand = (RegisterOperand)espOperand;
Assert.Equal(RegisterIndex.Sp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ESP)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x10, immediateOperand.Value);
}
/// <summary>
@ -141,15 +243,33 @@ public class SubInstructionTests
// Arrange
// SUB EAX, EBX (29 D8) - Subtract EBX from EAX
byte[] codeBuffer = new byte[] { 0x29, 0xD8 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
Assert.Equal("eax, ebx", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (EBX)
var ebxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand2 = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EBX)
}
/// <summary>
@ -161,15 +281,34 @@ public class SubInstructionTests
// Arrange
// SUB [EBX+0x10], ECX (29 4B 10) - Subtract ECX from memory at [EBX+0x10]
byte[] codeBuffer = new byte[] { 0x29, 0x4B, 0x10 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
Assert.Equal("dword ptr [ebx+0x10], ecx", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (memory operand)
var memoryOperand = instruction.StructuredOperands[0];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand);
var memory = (DisplacementMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.B, memory.BaseRegister); // Base register is EBX
Assert.Equal(0x10, memory.Displacement); // Displacement is 0x10
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
// Check the second operand (ECX)
var ecxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -181,15 +320,33 @@ public class SubInstructionTests
// Arrange
// SUB EBX, EAX (2B D8) - Subtract EAX from EBX
byte[] codeBuffer = new byte[] { 0x2B, 0xD8 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
Assert.Equal("ebx, eax", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EBX)
var ebxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EBX)
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand2 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -201,15 +358,34 @@ public class SubInstructionTests
// Arrange
// SUB ECX, [EBX+0x10] (2B 4B 10) - Subtract memory at [EBX+0x10] from ECX
byte[] codeBuffer = new byte[] { 0x2B, 0x4B, 0x10 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("sub", instruction.Mnemonic);
Assert.Equal("ecx, dword ptr [ebx+0x10]", instruction.Operands);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (memory operand)
var memoryOperand = instruction.StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand);
var memory = (DisplacementMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.B, memory.BaseRegister); // Base register is EBX
Assert.Equal(0x10, memory.Displacement); // Displacement is 0x10
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
}
/// <summary>
@ -232,15 +408,69 @@ public class SubInstructionTests
Assert.Equal(3, instructions.Count);
// First instruction: SUB ESP, 0x10
Assert.Equal("sub", instructions[0].Mnemonic);
Assert.Equal("esp, 0x10", instructions[0].Operands);
var instruction1 = instructions[0];
Assert.NotNull(instruction1);
Assert.Equal(InstructionType.Sub, instruction1.Type);
// Check that we have two operands
Assert.Equal(2, instruction1.StructuredOperands.Count);
// Check the first operand (ESP)
var espOperand = instruction1.StructuredOperands[0];
Assert.IsType<RegisterOperand>(espOperand);
var registerOperand = (RegisterOperand)espOperand;
Assert.Equal(RegisterIndex.Sp, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (ESP)
// Check the second operand (immediate value)
var immOperand = instruction1.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x10, immediateOperand.Value);
// Second instruction: SUB EAX, EBX
Assert.Equal("sub", instructions[1].Mnemonic);
Assert.Equal("eax, ebx", instructions[1].Operands);
var instruction2 = instructions[1];
Assert.NotNull(instruction2);
Assert.Equal(InstructionType.Sub, instruction2.Type);
// Check that we have two operands
Assert.Equal(2, instruction2.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction2.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand2 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (EBX)
var ebxOperand = instruction2.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand3 = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand3.Register);
Assert.Equal(32, registerOperand3.Size); // Validate that it's a 32-bit register (EBX)
// Third instruction: SUB ECX, [EBP-4]
Assert.Equal("sub", instructions[2].Mnemonic);
Assert.Equal("ecx, dword ptr [ebp-0x04]", instructions[2].Operands);
var instruction3 = instructions[2];
Assert.NotNull(instruction3);
Assert.Equal(InstructionType.Sub, instruction3.Type);
// Check that we have two operands
Assert.Equal(2, instruction3.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction3.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand4 = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand4.Register);
Assert.Equal(32, registerOperand4.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (memory operand)
var memoryOperand = instruction3.StructuredOperands[1];
Assert.IsType<DisplacementMemoryOperand>(memoryOperand);
var memory = (DisplacementMemoryOperand)memoryOperand;
Assert.Equal(RegisterIndex.Bp, memory.BaseRegister); // Base register is EBP
Assert.Equal(-4, memory.Displacement); // Displacement is -4
Assert.Equal(32, memory.Size); // Memory size is 32 bits (DWORD)
}
}

View File

@ -1,24 +1,48 @@
using X86Disassembler.X86;
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
/// <summary>
/// Tests for SUB r/m8, imm8 instruction
/// </summary>
public class SubRm8Imm8Tests
{
/// <summary>
/// Tests the SUB r8, imm8 instruction with register operand
/// </summary>
[Fact]
public void SubRm8Imm8_Decodes_Correctly()
{
// Arrange
// SUB BL, 0x42
byte[] codeBuffer = new byte[] { 0x80, 0xeb, 0x42 };
var decoder = new Disassembler(codeBuffer, 0x1000);
var disassembler = new Disassembler(codeBuffer, 0x1000);
// Act
var instructions = decoder.Disassemble();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
Assert.NotNull(instructions[0]);
Assert.Equal("sub", instructions[0].Mnemonic);
Assert.Equal("bl, 0x42", instructions[0].Operands);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal(InstructionType.Sub, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (BL)
var blOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(blOperand);
var registerOperand = (RegisterOperand)blOperand;
Assert.Equal(RegisterIndex.B, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (BL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
Assert.Equal(8, immediateOperand.Size); // Validate that it's an 8-bit immediate
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -17,15 +18,33 @@ public class TestInstructionHandlerTests
// TEST ECX, EAX (85 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[] { 0x85, 0xC1 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("ecx, eax", instruction.Operands);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand1 = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand2 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -38,15 +57,33 @@ public class TestInstructionHandlerTests
// TEST CL, AL (84 C1) - ModR/M byte C1 = 11 000 001 (mod=3, reg=0, rm=1)
// mod=3 means direct register addressing, reg=0 is AL, rm=1 is CL
byte[] codeBuffer = new byte[] { 0x84, 0xC1 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("cl, al", instruction.Operands);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (CL)
var clOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(clOperand);
var registerOperand1 = (RegisterOperand)clOperand;
Assert.Equal(RegisterIndex.C, registerOperand1.Register);
Assert.Equal(8, registerOperand1.Size); // Validate that it's an 8-bit register (CL)
// Check the second operand (AL)
var alOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand2 = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand2.Register);
Assert.Equal(8, registerOperand2.Size); // Validate that it's an 8-bit register (AL)
}
/// <summary>
@ -58,16 +95,33 @@ public class TestInstructionHandlerTests
// Arrange
// TEST AL, 0x42 (A8 42)
byte[] codeBuffer = new byte[] { 0xA8, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
// The handler should produce "al, 0xXX" as the operands
Assert.Equal("al, 0x42", instruction.Operands);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
Assert.Equal(8, immediateOperand.Size); // Validate that it's an 8-bit immediate
}
/// <summary>
@ -79,18 +133,35 @@ public class TestInstructionHandlerTests
// Arrange
// TEST EAX, 0x12345678 (A9 78 56 34 12)
byte[] codeBuffer = new byte[] { 0xA9, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
// The handler should produce "eax, 0xXXXXXXXX" as the operands
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
/// <summary>
/// Tests the TestImmWithRm8Handler for decoding TEST r/m8, imm8 instructions
/// </summary>
@ -101,15 +172,33 @@ public class TestInstructionHandlerTests
// TEST AH, 0x01 (F6 C4 01) - ModR/M byte C4 = 11 000 100 (mod=3, reg=0, rm=4)
// mod=3 means direct register addressing, reg=0 indicates TEST operation, rm=4 is AH
byte[] codeBuffer = new byte[] { 0xF6, 0xC4, 0x01 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("ah, 0x01", instruction.Operands);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AH)
var ahOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ahOperand);
var registerOperand = (RegisterOperand)ahOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AH)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x01U, immediateOperand.Value);
Assert.Equal(8, immediateOperand.Size); // Validate that it's an 8-bit immediate
}
/// <summary>
@ -122,14 +211,32 @@ public class TestInstructionHandlerTests
// TEST EDI, 0x12345678 (F7 C7 78 56 34 12) - ModR/M byte C7 = 11 000 111 (mod=3, reg=0, rm=7)
// mod=3 means direct register addressing, reg=0 indicates TEST operation, rm=7 is EDI
byte[] codeBuffer = new byte[] { 0xF7, 0xC7, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("test", instruction.Mnemonic);
Assert.Equal("edi, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Test, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EDI)
var ediOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ediOperand);
var registerOperand = (RegisterOperand)ediOperand;
Assert.Equal(RegisterIndex.D, registerOperand.Register);
Assert.Equal(32, registerOperand.Size); // Validate that it's a 32-bit register (EDI)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -23,8 +24,10 @@ public class XchgInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("nop", instruction.Mnemonic);
Assert.Equal("", instruction.Operands);
Assert.Equal(InstructionType.Nop, instruction.Type);
// Check that we have no operands for NOP
Assert.Empty(instruction.StructuredOperands);
}
/// <summary>
@ -43,8 +46,24 @@ public class XchgInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("xchg", instruction.Mnemonic);
Assert.Equal("eax, ecx", instruction.Operands);
Assert.Equal(InstructionType.Xchg, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (ECX)
var ecxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand2 = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -63,8 +82,24 @@ public class XchgInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("xchg", instruction.Mnemonic);
Assert.Equal("eax, edx", instruction.Operands);
Assert.Equal(InstructionType.Xchg, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (EDX)
var edxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(edxOperand);
var registerOperand2 = (RegisterOperand)edxOperand;
Assert.Equal(RegisterIndex.D, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EDX)
}
/// <summary>
@ -83,8 +118,24 @@ public class XchgInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("xchg", instruction.Mnemonic);
Assert.Equal("eax, ebx", instruction.Operands);
Assert.Equal(InstructionType.Xchg, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (EBX)
var ebxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ebxOperand);
var registerOperand2 = (RegisterOperand)ebxOperand;
Assert.Equal(RegisterIndex.B, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EBX)
}
/// <summary>
@ -103,8 +154,24 @@ public class XchgInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("xchg", instruction.Mnemonic);
Assert.Equal("eax, esp", instruction.Operands);
Assert.Equal(InstructionType.Xchg, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (ESP)
var espOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(espOperand);
var registerOperand2 = (RegisterOperand)espOperand;
Assert.Equal(RegisterIndex.Sp, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (ESP)
}
/// <summary>
@ -123,8 +190,24 @@ public class XchgInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("xchg", instruction.Mnemonic);
Assert.Equal("eax, ebp", instruction.Operands);
Assert.Equal(InstructionType.Xchg, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (EBP)
var ebpOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ebpOperand);
var registerOperand2 = (RegisterOperand)ebpOperand;
Assert.Equal(RegisterIndex.Bp, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EBP)
}
/// <summary>
@ -143,8 +226,24 @@ public class XchgInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("xchg", instruction.Mnemonic);
Assert.Equal("eax, esi", instruction.Operands);
Assert.Equal(InstructionType.Xchg, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (ESI)
var esiOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(esiOperand);
var registerOperand2 = (RegisterOperand)esiOperand;
Assert.Equal(RegisterIndex.Si, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (ESI)
}
/// <summary>
@ -163,8 +262,24 @@ public class XchgInstructionTests
// Assert
Assert.NotNull(instruction);
Assert.Equal("xchg", instruction.Mnemonic);
Assert.Equal("eax, edi", instruction.Operands);
Assert.Equal(InstructionType.Xchg, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (EDI)
var ediOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ediOperand);
var registerOperand2 = (RegisterOperand)ediOperand;
Assert.Equal(RegisterIndex.Di, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EDI)
}
/// <summary>
@ -187,12 +302,28 @@ public class XchgInstructionTests
// First three instructions should be NOPs
for (int i = 0; i < 3; i++)
{
Assert.Equal("nop", instructions[i].Mnemonic);
Assert.Equal("", instructions[i].Operands);
Assert.Equal(InstructionType.Nop, instructions[i].Type);
Assert.Empty(instructions[i].StructuredOperands);
}
// Last instruction should be XCHG EAX, ECX
Assert.Equal("xchg", instructions[3].Mnemonic);
Assert.Equal("eax, ecx", instructions[3].Operands);
Assert.Equal(InstructionType.Xchg, instructions[3].Type);
// Check that we have two operands
Assert.Equal(2, instructions[3].StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instructions[3].StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (ECX)
var ecxOperand = instructions[3].StructuredOperands[1];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand2 = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (ECX)
}
}

View File

@ -1,4 +1,5 @@
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests.InstructionTests;
@ -17,15 +18,33 @@ public class XorInstructionTests
// XOR EAX, ECX (33 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[] { 0x33, 0xC1 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("xor", instruction.Mnemonic);
Assert.Equal("eax, ecx", instruction.Operands);
Assert.Equal(InstructionType.Xor, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (ECX)
var ecxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand2 = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (ECX)
}
/// <summary>
@ -38,15 +57,33 @@ public class XorInstructionTests
// XOR ECX, EAX (31 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[] { 0x31, 0xC1 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("xor", instruction.Mnemonic);
Assert.Equal("ecx, eax", instruction.Operands);
Assert.Equal(InstructionType.Xor, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (ECX)
var ecxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(ecxOperand);
var registerOperand1 = (RegisterOperand)ecxOperand;
Assert.Equal(RegisterIndex.C, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (ECX)
// Check the second operand (EAX)
var eaxOperand = instruction.StructuredOperands[1];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand2 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand2.Register);
Assert.Equal(32, registerOperand2.Size); // Validate that it's a 32-bit register (EAX)
}
/// <summary>
@ -58,15 +95,33 @@ public class XorInstructionTests
// Arrange
// XOR AL, 0x42 (34 42)
byte[] codeBuffer = new byte[] { 0x34, 0x42 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("xor", instruction.Mnemonic);
Assert.Equal("al, 0x42", instruction.Operands);
Assert.Equal(InstructionType.Xor, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand);
var registerOperand = (RegisterOperand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x42U, immediateOperand.Value);
Assert.Equal(8, immediateOperand.Size); // Validate that it's an 8-bit immediate
}
/// <summary>
@ -78,14 +133,32 @@ public class XorInstructionTests
// Arrange
// XOR EAX, 0x12345678 (35 78 56 34 12)
byte[] codeBuffer = new byte[] { 0x35, 0x78, 0x56, 0x34, 0x12 };
var decoder = new InstructionDecoder(codeBuffer, codeBuffer.Length);
var disassembler = new Disassembler(codeBuffer, 0);
// Act
var instruction = decoder.DecodeInstruction();
var instructions = disassembler.Disassemble();
// Assert
Assert.Single(instructions);
var instruction = instructions[0];
Assert.NotNull(instruction);
Assert.Equal("xor", instruction.Mnemonic);
Assert.Equal("eax, 0x12345678", instruction.Operands);
Assert.Equal(InstructionType.Xor, instruction.Type);
// Check that we have two operands
Assert.Equal(2, instruction.StructuredOperands.Count);
// Check the first operand (EAX)
var eaxOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(eaxOperand);
var registerOperand1 = (RegisterOperand)eaxOperand;
Assert.Equal(RegisterIndex.A, registerOperand1.Register);
Assert.Equal(32, registerOperand1.Size); // Validate that it's a 32-bit register (EAX)
// Check the second operand (0x12345678)
var immOperand = instruction.StructuredOperands[1];
Assert.IsType<ImmediateOperand>(immOperand);
var immediateOperand = (ImmediateOperand)immOperand;
Assert.Equal(0x12345678U, immediateOperand.Value);
Assert.Equal(32, immediateOperand.Size); // Validate that it's a 32-bit immediate
}
}

View File

@ -3,6 +3,7 @@ using System.Reflection;
using CsvHelper;
using CsvHelper.Configuration;
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
using Xunit.Abstractions;
namespace X86DisassemblerTests;
@ -82,74 +83,92 @@ public class RawFromFileDisassemblyTests(ITestOutputHelper output)
var expected = test.Instructions[i];
var actual = disassembledInstructions[i];
if (expected.Mnemonic != actual.Mnemonic)
// Compare instruction type instead of mnemonic
if (expected.Type != actual.Type)
{
AssertFailWithReason(
index,
file,
test,
disassembledInstructions,
"Mnemonic mismatch"
$"Type mismatch: Expected {expected.Type}, got {actual.Type}"
);
}
if (expected.Operands != actual.Operands)
// For operands, we need to do a string comparison since the CSV contains string operands
// and we now have structured operands in the actual instruction
string actualOperandsString = string.Join(", ", actual.StructuredOperands);
if (!CompareOperands(expected.Operands, actualOperandsString))
{
AssertFailWithReason(
index,
file,
test,
disassembledInstructions,
"Operands mismatch"
$"Operands mismatch: Expected '{expected.Operands}', got '{actualOperandsString}'"
);
}
}
output.WriteLine(
$"Test succeeded {index} of file {file}: {test.RawBytes}.\n" +
$"Instruction count \"{test.Instructions.Count}\".\n" +
$"{string.Join("\n", test.Instructions.Select(x => $"{x.Mnemonic} {x.Operands}"))}\n"
);
}
}
private static void AssertFailWithReason(int index, string file, TestFromFileEntry test, List<Instruction> disassembledInstructions, string reason)
// Compare operands with some flexibility since the string representation might be slightly different
private bool CompareOperands(string expected, string actual)
{
Assert.Fail(
$"Failed verifying test {index} of file {file}: {test.RawBytes}. {reason}.\n" +
$"Expected \"{test.Instructions.Count}\", but got \"{disassembledInstructions.Count}\".\n" +
$"\n" +
$"Expected instructions:\n" +
$"{string.Join("\n", test.Instructions.Select(x => $"{x.Mnemonic} {x.Operands}"))}\n" +
$"\n" +
$"Disassembled instructions:\n" +
$"{string.Join("\n", disassembledInstructions)}"
);
// Normalize strings for comparison
expected = NormalizeOperandString(expected);
actual = NormalizeOperandString(actual);
return expected == actual;
}
// Normalize operand strings to handle slight formatting differences
private string NormalizeOperandString(string operands)
{
if (string.IsNullOrEmpty(operands))
return string.Empty;
// Remove all spaces
operands = operands.Replace(" ", "");
// Convert to lowercase
operands = operands.ToLowerInvariant();
// Normalize hex values (remove 0x prefix if present)
operands = operands.Replace("0x", "");
return operands;
}
private void AssertFailWithReason(int index, string file, TestFromFileEntry test, List<Instruction> disassembledInstructions, string reason)
{
output.WriteLine($"Test {index} in {file} failed: {reason}");
output.WriteLine($"Raw bytes: {test.RawBytes}");
output.WriteLine("Expected instructions:");
foreach (var instruction in test.Instructions)
{
output.WriteLine($" {instruction.Mnemonic} {instruction.Operands}");
}
output.WriteLine("Actual instructions:");
foreach (var instruction in disassembledInstructions)
{
output.WriteLine($" {instruction.Type} {string.Join(", ", instruction.StructuredOperands)}");
}
Assert.True(false, reason);
}
/// <summary>
/// Converts a hex string to a byte array
/// </summary>
/// <param name="hex">The hex string to convert</param>
/// <returns>The byte array</returns>
private static byte[] HexStringToByteArray(string hex)
{
// Remove any non-hex characters if present
hex = hex.Replace(" ", "").Replace("-", "");
// Remove any spaces or other formatting characters
hex = hex.Replace(" ", "").Replace("-", "").Replace("0x", "");
// Create a byte array
// Create a byte array that will hold the converted hex string
byte[] bytes = new byte[hex.Length / 2];
// Convert each pair of hex characters to a byte using spans for better performance
ReadOnlySpan<char> hexSpan = hex.AsSpan();
for (int i = 0; i < hexSpan.Length; i += 2)
// Convert each pair of hex characters to a byte
for (int i = 0; i < hex.Length; i += 2)
{
// Parse two characters at a time using spans
if (!byte.TryParse(hexSpan.Slice(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bytes[i / 2]))
{
throw new FormatException($"Invalid hex string at position {i}: {hexSpan.Slice(i, 2).ToString()}");
}
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;

View File

@ -1,4 +1,6 @@
using CsvHelper.Configuration;
using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86DisassemblerTests;
@ -20,9 +22,13 @@ public class TestFromFileEntry
public class TestFromFileInstruction
{
// Keep the old properties for CSV deserialization
public string Mnemonic { get; set; } = string.Empty;
public string Operands { get; set; } = string.Empty;
// Add new properties for comparison with actual Instruction objects
public InstructionType Type => ConvertMnemonicToType(Mnemonic);
// Parameterless constructor required by CsvHelper
public TestFromFileInstruction()
{
@ -33,6 +39,40 @@ public class TestFromFileInstruction
Mnemonic = mnemonic;
Operands = operands;
}
// Helper method to convert mnemonic string to InstructionType
private InstructionType ConvertMnemonicToType(string mnemonic)
{
// Convert mnemonic to InstructionType
return mnemonic.ToLowerInvariant() switch
{
"add" => InstructionType.Add,
"adc" => InstructionType.Adc,
"and" => InstructionType.And,
"call" => InstructionType.Call,
"cmp" => InstructionType.Cmp,
"dec" => InstructionType.Dec,
"inc" => InstructionType.Inc,
"int3" => InstructionType.Int,
"jmp" => InstructionType.Jmp,
"jz" => InstructionType.Jz,
"jnz" => InstructionType.Jnz,
"jge" => InstructionType.Jge,
"lea" => InstructionType.Lea,
"mov" => InstructionType.Mov,
"nop" => InstructionType.Nop,
"or" => InstructionType.Or,
"pop" => InstructionType.Pop,
"push" => InstructionType.Push,
"ret" => InstructionType.Ret,
"sbb" => InstructionType.Sbb,
"sub" => InstructionType.Sub,
"test" => InstructionType.Test,
"xchg" => InstructionType.Xchg,
"xor" => InstructionType.Xor,
_ => InstructionType.Unknown
};
}
}
public sealed class TestFromFileEntryMap : ClassMap<TestFromFileEntry>