mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-05-19 03:41:18 +03:00
Refactor SUB handlers
This commit is contained in:
parent
a04a16af7d
commit
e91a0223f7
@ -20,10 +20,7 @@
|
||||
<Project Location="C:\Projects\CSharp\ParkanPlayground\X86DisassemblerTests" Presentation="&lt;X86DisassemblerTests&gt;" />
|
||||
</And>
|
||||
</SessionState></s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=a2ceb6d1_002D1e39_002D4063_002Dbbb5_002D2f7a30b9386e/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="RunTestsOnJson" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5::net8.0::X86DisassemblerTests.GeneralDisassemblerInstructionTests.RunTestsOnJson</TestId>
|
||||
<TestId>xUnit::D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5::net8.0::X86DisassemblerTests.PushRegTests.PushRegTests</TestId>
|
||||
<TestId>xUnit::D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5::net8.0::X86DisassemblerTests.RawFromFileDisassemblyTests</TestId>
|
||||
</TestAncestor>
|
||||
</SessionState></s:String></wpf:ResourceDictionary>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=ea256cae_002Df513_002D419e_002D992a_002D13b500b3c433/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &lt;X86DisassemblerTests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="C:\Projects\CSharp\ParkanPlayground\X86DisassemblerTests" Presentation="&lt;X86DisassemblerTests&gt;" />
|
||||
</SessionState></s:String>
|
||||
</wpf:ResourceDictionary>
|
54
X86Disassembler/X86/Handlers/Sub/SubAlImm8Handler.cs
Normal file
54
X86Disassembler/X86/Handlers/Sub/SubAlImm8Handler.cs
Normal file
@ -0,0 +1,54 @@
|
||||
namespace X86Disassembler.X86.Handlers.Sub;
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SUB AL, imm8 instruction (0x2C)
|
||||
/// </summary>
|
||||
public class SubAlImm8Handler : InstructionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SubAlImm8Handler class
|
||||
/// </summary>
|
||||
/// <param name="codeBuffer">The buffer containing the code to decode</param>
|
||||
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||
/// <param name="length">The length of the buffer</param>
|
||||
public SubAlImm8Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
: base(codeBuffer, decoder, length)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this handler can decode the given opcode
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode to check</param>
|
||||
/// <returns>True if this handler can decode the opcode</returns>
|
||||
public override bool CanHandle(byte opcode)
|
||||
{
|
||||
return opcode == 0x2C;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a SUB AL, imm8 instruction
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode of the instruction</param>
|
||||
/// <param name="instruction">The instruction object to populate</param>
|
||||
/// <returns>True if the instruction was successfully decoded</returns>
|
||||
public override bool Decode(byte opcode, Instruction instruction)
|
||||
{
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the immediate byte
|
||||
byte imm8 = CodeBuffer[position++];
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
// Set the instruction information
|
||||
instruction.Mnemonic = "sub";
|
||||
instruction.Operands = $"al, 0x{imm8:X2}";
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -37,28 +37,21 @@ public class SubAxImm16Handler : InstructionHandler
|
||||
{
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
|
||||
// Check if we have enough bytes for the immediate value
|
||||
if (position + 1 >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Read the immediate value (16-bit)
|
||||
byte lowByte = CodeBuffer[position++];
|
||||
byte highByte = CodeBuffer[position++];
|
||||
|
||||
// Combine the bytes into a 16-bit value
|
||||
ushort immediate = (ushort)((highByte << 8) | lowByte);
|
||||
|
||||
// Update the decoder position
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
var immediate = Decoder.ReadUInt16();
|
||||
|
||||
// Set the operands (note: we use "eax" instead of "ax" to match the disassembler's output)
|
||||
instruction.Operands = $"eax, 0x{immediate:X4}";
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -36,32 +36,27 @@ public class SubImmFromRm16Handler : InstructionHandler
|
||||
public override bool Decode(byte opcode, Instruction instruction)
|
||||
{
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the ModR/M byte
|
||||
byte modRM = CodeBuffer[position++];
|
||||
|
||||
|
||||
// Extract the fields from the ModR/M byte
|
||||
byte mod = (byte)((modRM & 0xC0) >> 6);
|
||||
byte reg = (byte)((modRM & 0x38) >> 3);
|
||||
byte rm = (byte)(modRM & 0x07);
|
||||
|
||||
var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Check if this is a SUB instruction (reg field must be 5)
|
||||
if (reg != 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
|
||||
// Update the decoder position
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
|
||||
// For mod == 3, the r/m field specifies a register
|
||||
string destination;
|
||||
if (mod == 3)
|
||||
@ -73,43 +68,37 @@ public class SubImmFromRm16Handler : InstructionHandler
|
||||
{
|
||||
// Get the memory operand string
|
||||
destination = ModRMDecoder.DecodeModRM(mod, rm, false);
|
||||
|
||||
|
||||
// Replace "dword" with "word" in the memory operand
|
||||
destination = destination.Replace("dword", "word");
|
||||
}
|
||||
|
||||
|
||||
// Get the current position after processing the ModR/M byte
|
||||
position = Decoder.GetPosition();
|
||||
|
||||
|
||||
// Check if we have enough bytes for the immediate value
|
||||
if (position + 1 >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Read the immediate value (16-bit)
|
||||
byte lowByte = CodeBuffer[position++];
|
||||
byte highByte = CodeBuffer[position++];
|
||||
|
||||
// Combine the bytes into a 16-bit value
|
||||
ushort immediate = (ushort)((highByte << 8) | lowByte);
|
||||
|
||||
// Update the decoder position
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
ushort immediate = Decoder.ReadUInt16();
|
||||
|
||||
// Set the operands (note: we use 32-bit register names to match the disassembler's output)
|
||||
if (mod == 3)
|
||||
{
|
||||
// For register operands, use the 32-bit register name
|
||||
string reg32Name = destination.Replace("ax", "eax")
|
||||
.Replace("bx", "ebx")
|
||||
.Replace("cx", "ecx")
|
||||
.Replace("dx", "edx")
|
||||
.Replace("sp", "esp")
|
||||
.Replace("bp", "ebp")
|
||||
.Replace("si", "esi")
|
||||
.Replace("di", "edi");
|
||||
|
||||
string reg32Name = destination
|
||||
.Replace("ax", "eax")
|
||||
.Replace("bx", "ebx")
|
||||
.Replace("cx", "ecx")
|
||||
.Replace("dx", "edx")
|
||||
.Replace("sp", "esp")
|
||||
.Replace("bp", "ebp")
|
||||
.Replace("si", "esi")
|
||||
.Replace("di", "edi");
|
||||
|
||||
instruction.Operands = $"{reg32Name}, 0x{immediate:X4}";
|
||||
}
|
||||
else
|
||||
@ -117,7 +106,7 @@ public class SubImmFromRm16Handler : InstructionHandler
|
||||
// For memory operands, keep the memory operand as is
|
||||
instruction.Operands = $"{destination}, 0x{immediate:X4}";
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -36,32 +36,24 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
|
||||
public override bool Decode(byte opcode, Instruction instruction)
|
||||
{
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the ModR/M byte
|
||||
byte modRM = CodeBuffer[position++];
|
||||
|
||||
|
||||
// Extract the fields from the ModR/M byte
|
||||
byte mod = (byte)((modRM & 0xC0) >> 6);
|
||||
byte reg = (byte)((modRM & 0x38) >> 3);
|
||||
byte rm = (byte)(modRM & 0x07);
|
||||
|
||||
var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Check if this is a SUB instruction (reg field must be 5)
|
||||
if (reg != 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
// Update the decoder position
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
|
||||
// For mod == 3, the r/m field specifies a register
|
||||
string destination;
|
||||
if (mod == 3)
|
||||
@ -73,39 +65,37 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
|
||||
{
|
||||
// Get the memory operand string
|
||||
destination = ModRMDecoder.DecodeModRM(mod, rm, false);
|
||||
|
||||
|
||||
// Replace "dword" with "word" in the memory operand
|
||||
destination = destination.Replace("dword", "word");
|
||||
}
|
||||
|
||||
|
||||
// Get the current position after processing the ModR/M byte
|
||||
position = Decoder.GetPosition();
|
||||
|
||||
|
||||
// Check if we have enough bytes for the immediate value
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Read the immediate value (8-bit)
|
||||
byte immediate = CodeBuffer[position++];
|
||||
|
||||
// Update the decoder position
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
byte immediate = Decoder.ReadByte();
|
||||
|
||||
// Set the operands (note: we use 32-bit register names to match the disassembler's output)
|
||||
if (mod == 3)
|
||||
{
|
||||
// For register operands, use the 32-bit register name
|
||||
string reg32Name = destination.Replace("ax", "eax")
|
||||
.Replace("bx", "ebx")
|
||||
.Replace("cx", "ecx")
|
||||
.Replace("dx", "edx")
|
||||
.Replace("sp", "esp")
|
||||
.Replace("bp", "ebp")
|
||||
.Replace("si", "esi")
|
||||
.Replace("di", "edi");
|
||||
|
||||
string reg32Name = destination
|
||||
.Replace("ax", "eax")
|
||||
.Replace("bx", "ebx")
|
||||
.Replace("cx", "ecx")
|
||||
.Replace("dx", "edx")
|
||||
.Replace("sp", "esp")
|
||||
.Replace("bp", "ebp")
|
||||
.Replace("si", "esi")
|
||||
.Replace("di", "edi");
|
||||
|
||||
instruction.Operands = $"{reg32Name}, 0x{immediate:X2}";
|
||||
}
|
||||
else
|
||||
@ -113,7 +103,7 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
|
||||
// For memory operands, keep the memory operand as is
|
||||
instruction.Operands = $"{destination}, 0x{immediate:X2}";
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,11 @@ public class SubImmFromRm32Handler : InstructionHandler
|
||||
/// <param name="codeBuffer">The buffer containing the code to decode</param>
|
||||
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||
/// <param name="length">The length of the buffer</param>
|
||||
public SubImmFromRm32Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
public SubImmFromRm32Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
: base(codeBuffer, decoder, length)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this handler can decode the given opcode
|
||||
/// </summary>
|
||||
@ -25,18 +25,18 @@ public class SubImmFromRm32Handler : InstructionHandler
|
||||
{
|
||||
if (opcode != 0x81)
|
||||
return false;
|
||||
|
||||
|
||||
// Check if the reg field of the ModR/M byte is 5 (SUB)
|
||||
int position = Decoder.GetPosition();
|
||||
if (position >= Length)
|
||||
return false;
|
||||
|
||||
|
||||
byte modRM = CodeBuffer[position];
|
||||
byte reg = (byte)((modRM & 0x38) >> 3);
|
||||
|
||||
byte reg = (byte) ((modRM & 0x38) >> 3);
|
||||
|
||||
return reg == 5; // 5 = SUB
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a SUB r/m32, imm32 instruction
|
||||
/// </summary>
|
||||
@ -47,53 +47,43 @@ public class SubImmFromRm32Handler : InstructionHandler
|
||||
{
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Read the ModR/M byte
|
||||
byte modRM = CodeBuffer[position++];
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
|
||||
// Extract the fields from the ModR/M byte
|
||||
byte mod = (byte)((modRM & 0xC0) >> 6);
|
||||
byte reg = (byte)((modRM & 0x38) >> 3); // Should be 5 for SUB
|
||||
byte rm = (byte)(modRM & 0x07);
|
||||
|
||||
// Let the ModRMDecoder handle the ModR/M byte and any additional bytes (SIB, displacement)
|
||||
// This will update the decoder position to point after the ModR/M and any additional bytes
|
||||
string destOperand = ModRMDecoder.DecodeModRM(mod, rm, false);
|
||||
|
||||
var (mod, reg, rm, destOperand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Get the updated position after ModR/M decoding
|
||||
position = Decoder.GetPosition();
|
||||
|
||||
|
||||
// Read the immediate value
|
||||
if (position + 3 >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Read the immediate value in little-endian format
|
||||
byte b0 = CodeBuffer[position];
|
||||
byte b1 = CodeBuffer[position + 1];
|
||||
byte b2 = CodeBuffer[position + 2];
|
||||
byte b3 = CodeBuffer[position + 3];
|
||||
|
||||
// Format the immediate value as expected by the tests (0x12345678)
|
||||
// Note: Always use the same format regardless of operand type to match test expectations
|
||||
string immStr = $"0x{b3:X2}{b2:X2}{b1:X2}{b0:X2}";
|
||||
|
||||
var imm = Decoder.ReadUInt32();
|
||||
|
||||
// Format the immediate value
|
||||
string immStr = $"0x{imm:X8}";
|
||||
|
||||
// Advance the position past the immediate value
|
||||
position += 4;
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
|
||||
// Set the operands
|
||||
instruction.Operands = $"{destOperand}, {immStr}";
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,11 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
|
||||
/// <param name="codeBuffer">The buffer containing the code to decode</param>
|
||||
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||
/// <param name="length">The length of the buffer</param>
|
||||
public SubImmFromRm32SignExtendedHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
public SubImmFromRm32SignExtendedHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
: base(codeBuffer, decoder, length)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this handler can decode the given opcode
|
||||
/// </summary>
|
||||
@ -25,18 +25,18 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
|
||||
{
|
||||
if (opcode != 0x83)
|
||||
return false;
|
||||
|
||||
|
||||
// Check if the reg field of the ModR/M byte is 5 (SUB)
|
||||
int position = Decoder.GetPosition();
|
||||
if (position >= Length)
|
||||
return false;
|
||||
|
||||
|
||||
byte modRM = CodeBuffer[position];
|
||||
byte reg = (byte)((modRM & 0x38) >> 3);
|
||||
|
||||
byte reg = (byte) ((modRM & 0x38) >> 3);
|
||||
|
||||
return reg == 5; // 5 = SUB
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a SUB r/m32, imm8 (sign-extended) instruction
|
||||
/// </summary>
|
||||
@ -47,49 +47,41 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
|
||||
{
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the ModR/M byte
|
||||
byte modRM = CodeBuffer[position++];
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
|
||||
// Extract the fields from the ModR/M byte
|
||||
byte mod = (byte)((modRM & 0xC0) >> 6);
|
||||
byte reg = (byte)((modRM & 0x38) >> 3); // Should be 5 for SUB
|
||||
byte rm = (byte)(modRM & 0x07);
|
||||
|
||||
var (mod, reg, rm, destOperand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Let the ModRMDecoder handle the ModR/M byte and any additional bytes (SIB, displacement)
|
||||
// This will update the decoder position to point after the ModR/M and any additional bytes
|
||||
string destOperand = ModRMDecoder.DecodeModRM(mod, rm, false);
|
||||
|
||||
|
||||
// Get the updated position after ModR/M decoding
|
||||
position = Decoder.GetPosition();
|
||||
|
||||
|
||||
// Read the immediate value
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Read the immediate value as a signed byte and sign-extend it to 32 bits
|
||||
sbyte imm8 = (sbyte)CodeBuffer[position++];
|
||||
sbyte imm8 = (sbyte) Decoder.ReadByte();
|
||||
int imm32 = imm8; // Automatic sign extension from sbyte to int
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
|
||||
// Format the immediate value based on the operand type and value
|
||||
string immStr;
|
||||
|
||||
|
||||
// For memory operands, use a different format as expected by the tests
|
||||
if (mod != 3) // Memory operand
|
||||
{
|
||||
// For memory operands, use the actual value as specified in the test
|
||||
immStr = $"0x{(byte)imm8:X2}";
|
||||
immStr = $"0x{(byte) imm8:X2}";
|
||||
}
|
||||
else // Register operand
|
||||
{
|
||||
@ -97,18 +89,18 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
|
||||
if (imm8 < 0)
|
||||
{
|
||||
// For negative values, show the full 32-bit representation with 8-digit padding
|
||||
immStr = $"0x{(uint)imm32:X8}";
|
||||
immStr = $"0x{(uint) imm32:X8}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// For positive values, just show the value with 2-digit padding for consistency
|
||||
immStr = $"0x{(byte)imm8:X2}";
|
||||
immStr = $"0x{(byte) imm8:X2}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set the operands
|
||||
instruction.Operands = $"{destOperand}, {immStr}";
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
80
X86Disassembler/X86/Handlers/Sub/SubImmFromRm8Handler.cs
Normal file
80
X86Disassembler/X86/Handlers/Sub/SubImmFromRm8Handler.cs
Normal file
@ -0,0 +1,80 @@
|
||||
namespace X86Disassembler.X86.Handlers.Sub;
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SUB r/m8, imm8 instruction (0x80 /5)
|
||||
/// </summary>
|
||||
public class SubImmFromRm8Handler : InstructionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SubImmFromRm8Handler class
|
||||
/// </summary>
|
||||
/// <param name="codeBuffer">The buffer containing the code to decode</param>
|
||||
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||
/// <param name="length">The length of the buffer</param>
|
||||
public SubImmFromRm8Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
: base(codeBuffer, decoder, length)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this handler can decode the given opcode
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode to check</param>
|
||||
/// <returns>True if this handler can decode the opcode</returns>
|
||||
public override bool CanHandle(byte opcode)
|
||||
{
|
||||
if (opcode != 0x80)
|
||||
return false;
|
||||
|
||||
// Check if the reg field of the ModR/M byte is 5 (SUB)
|
||||
int position = Decoder.GetPosition();
|
||||
if (position >= Length)
|
||||
return false;
|
||||
|
||||
byte modRM = CodeBuffer[position];
|
||||
byte reg = (byte) ((modRM & 0x38) >> 3);
|
||||
|
||||
return reg == 5; // 5 = SUB
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a SUB r/m8, imm8 instruction
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode of the instruction</param>
|
||||
/// <param name="instruction">The instruction object to populate</param>
|
||||
/// <returns>True if the instruction was successfully decoded</returns>
|
||||
public override bool Decode(byte opcode, Instruction instruction)
|
||||
{
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
// Extract the fields from the ModR/M byte
|
||||
var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Read the immediate byte
|
||||
var position = Decoder.GetPosition();
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte imm8 = CodeBuffer[position++];
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
// Set the instruction information
|
||||
// For mod == 3, the operand is a register
|
||||
if (mod == 3)
|
||||
{
|
||||
string rmRegName = ModRMDecoder.GetRegisterName(rm, 8);
|
||||
instruction.Operands = $"{rmRegName}, 0x{imm8:X2}";
|
||||
}
|
||||
else // Memory operand
|
||||
{
|
||||
// Get the memory operand string
|
||||
string memOperand = ModRMDecoder.DecodeModRM(mod, rm, false);
|
||||
instruction.Operands = $"byte ptr {memOperand}, 0x{imm8:X2}";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -37,26 +37,20 @@ public class SubR16Rm16Handler : InstructionHandler
|
||||
{
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Read the ModR/M byte
|
||||
byte modRM = CodeBuffer[position++];
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
// Extract the fields from the ModR/M byte
|
||||
byte mod = (byte)((modRM & 0xC0) >> 6);
|
||||
byte reg = (byte)((modRM & 0x38) >> 3);
|
||||
byte rm = (byte)(modRM & 0x07);
|
||||
|
||||
var (mod, reg, rm, memOperand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Get register name (16-bit)
|
||||
string regName = ModRMDecoder.GetRegisterName(reg, 16);
|
||||
|
||||
|
||||
// For mod == 3, both operands are registers
|
||||
if (mod == 3)
|
||||
{
|
||||
@ -65,15 +59,12 @@ public class SubR16Rm16Handler : InstructionHandler
|
||||
}
|
||||
else // Memory operand
|
||||
{
|
||||
// Get the memory operand string (use false for is64Bit)
|
||||
string memOperand = ModRMDecoder.DecodeModRM(mod, rm, false);
|
||||
|
||||
// Replace "dword" with "word" in the memory operand
|
||||
memOperand = memOperand.Replace("dword", "word");
|
||||
|
||||
|
||||
instruction.Operands = $"{regName}, {memOperand}";
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -42,13 +42,7 @@ public class SubR32Rm32Handler : InstructionHandler
|
||||
}
|
||||
|
||||
// Read the ModR/M byte
|
||||
byte modRM = CodeBuffer[position++];
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
// Extract the fields from the ModR/M byte
|
||||
byte mod = (byte)((modRM & 0xC0) >> 6);
|
||||
byte reg = (byte)((modRM & 0x38) >> 3);
|
||||
byte rm = (byte)(modRM & 0x07);
|
||||
var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
@ -59,7 +53,6 @@ public class SubR32Rm32Handler : InstructionHandler
|
||||
// For memory operands, set the operand
|
||||
if (mod != 3) // Memory operand
|
||||
{
|
||||
string operand = ModRMDecoder.DecodeModRM(mod, rm, false);
|
||||
instruction.Operands = $"{regName}, {operand}";
|
||||
}
|
||||
else // Register operand
|
||||
@ -70,4 +63,4 @@ public class SubR32Rm32Handler : InstructionHandler
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
66
X86Disassembler/X86/Handlers/Sub/SubR8Rm8Handler.cs
Normal file
66
X86Disassembler/X86/Handlers/Sub/SubR8Rm8Handler.cs
Normal file
@ -0,0 +1,66 @@
|
||||
namespace X86Disassembler.X86.Handlers.Sub;
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SUB r8, r/m8 instruction (0x2A)
|
||||
/// </summary>
|
||||
public class SubR8Rm8Handler : InstructionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SubR8Rm8Handler class
|
||||
/// </summary>
|
||||
/// <param name="codeBuffer">The buffer containing the code to decode</param>
|
||||
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||
/// <param name="length">The length of the buffer</param>
|
||||
public SubR8Rm8Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
: base(codeBuffer, decoder, length)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this handler can decode the given opcode
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode to check</param>
|
||||
/// <returns>True if this handler can decode the opcode</returns>
|
||||
public override bool CanHandle(byte opcode)
|
||||
{
|
||||
return opcode == 0x2A;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a SUB r8, r/m8 instruction
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode of the instruction</param>
|
||||
/// <param name="instruction">The instruction object to populate</param>
|
||||
/// <returns>True if the instruction was successfully decoded</returns>
|
||||
public override bool Decode(byte opcode, Instruction instruction)
|
||||
{
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the ModR/M byte
|
||||
var (mod, reg, rm, memOperand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Get register name
|
||||
string regName = ModRMDecoder.GetRegisterName(reg, 8);
|
||||
|
||||
// For mod == 3, both operands are registers
|
||||
if (mod == 3)
|
||||
{
|
||||
string rmRegName = ModRMDecoder.GetRegisterName(rm, 8);
|
||||
instruction.Operands = $"{regName}, {rmRegName}";
|
||||
}
|
||||
else // Memory operand
|
||||
{
|
||||
instruction.Operands = $"{regName}, byte ptr {memOperand}";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
70
X86Disassembler/X86/Handlers/Sub/SubRm16R16Handler.cs
Normal file
70
X86Disassembler/X86/Handlers/Sub/SubRm16R16Handler.cs
Normal file
@ -0,0 +1,70 @@
|
||||
namespace X86Disassembler.X86.Handlers.Sub;
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SUB r/m16, r16 instruction (0x29 with 0x66 prefix)
|
||||
/// </summary>
|
||||
public class SubRm16R16Handler : InstructionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SubRm16R16Handler class
|
||||
/// </summary>
|
||||
/// <param name="codeBuffer">The buffer containing the code to decode</param>
|
||||
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||
/// <param name="length">The length of the buffer</param>
|
||||
public SubRm16R16Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
: base(codeBuffer, decoder, length)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this handler can decode the given opcode
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode to check</param>
|
||||
/// <returns>True if this handler can decode the opcode</returns>
|
||||
public override bool CanHandle(byte opcode)
|
||||
{
|
||||
// Check if the opcode is 0x29 and we have a 0x66 prefix
|
||||
return opcode == 0x29 && Decoder.HasOperandSizeOverridePrefix();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a SUB r/m16, r16 instruction
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode of the instruction</param>
|
||||
/// <param name="instruction">The instruction object to populate</param>
|
||||
/// <returns>True if the instruction was successfully decoded</returns>
|
||||
public override bool Decode(byte opcode, Instruction instruction)
|
||||
{
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the ModR/M byte
|
||||
var (mod, reg, rm, memOperand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Get register name (16-bit)
|
||||
string regName = ModRMDecoder.GetRegisterName(reg, 16);
|
||||
|
||||
// For mod == 3, both operands are registers
|
||||
if (mod == 3)
|
||||
{
|
||||
string rmRegName = ModRMDecoder.GetRegisterName(rm, 16);
|
||||
instruction.Operands = $"{rmRegName}, {regName}";
|
||||
}
|
||||
else // Memory operand
|
||||
{
|
||||
// Replace "dword" with "word" in the memory operand
|
||||
memOperand = memOperand.Replace("dword", "word");
|
||||
|
||||
instruction.Operands = $"{memOperand}, {regName}";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -42,13 +42,9 @@ public class SubRm32R32Handler : InstructionHandler
|
||||
}
|
||||
|
||||
// Read the ModR/M byte
|
||||
byte modRM = CodeBuffer[position++];
|
||||
Decoder.SetPosition(position);
|
||||
|
||||
|
||||
// Extract the fields from the ModR/M byte
|
||||
byte mod = (byte)((modRM & 0xC0) >> 6);
|
||||
byte reg = (byte)((modRM & 0x38) >> 3);
|
||||
byte rm = (byte)(modRM & 0x07);
|
||||
var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
@ -59,7 +55,6 @@ public class SubRm32R32Handler : InstructionHandler
|
||||
// For memory operands, set the operand
|
||||
if (mod != 3) // Memory operand
|
||||
{
|
||||
string operand = ModRMDecoder.DecodeModRM(mod, rm, false);
|
||||
instruction.Operands = $"{operand}, {regName}";
|
||||
}
|
||||
else // Register operand
|
||||
|
66
X86Disassembler/X86/Handlers/Sub/SubRm8R8Handler.cs
Normal file
66
X86Disassembler/X86/Handlers/Sub/SubRm8R8Handler.cs
Normal file
@ -0,0 +1,66 @@
|
||||
namespace X86Disassembler.X86.Handlers.Sub;
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SUB r/m8, r8 instruction (0x28)
|
||||
/// </summary>
|
||||
public class SubRm8R8Handler : InstructionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SubRm8R8Handler class
|
||||
/// </summary>
|
||||
/// <param name="codeBuffer">The buffer containing the code to decode</param>
|
||||
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||
/// <param name="length">The length of the buffer</param>
|
||||
public SubRm8R8Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
|
||||
: base(codeBuffer, decoder, length)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this handler can decode the given opcode
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode to check</param>
|
||||
/// <returns>True if this handler can decode the opcode</returns>
|
||||
public override bool CanHandle(byte opcode)
|
||||
{
|
||||
return opcode == 0x28;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a SUB r/m8, r8 instruction
|
||||
/// </summary>
|
||||
/// <param name="opcode">The opcode of the instruction</param>
|
||||
/// <param name="instruction">The instruction object to populate</param>
|
||||
/// <returns>True if the instruction was successfully decoded</returns>
|
||||
public override bool Decode(byte opcode, Instruction instruction)
|
||||
{
|
||||
// Set the mnemonic
|
||||
instruction.Mnemonic = "sub";
|
||||
|
||||
int position = Decoder.GetPosition();
|
||||
|
||||
if (position >= Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the ModR/M byte
|
||||
var (mod, reg, rm, memOperand) = ModRMDecoder.ReadModRM();
|
||||
|
||||
// Get register name
|
||||
string regName = ModRMDecoder.GetRegisterName(reg, 8);
|
||||
|
||||
// For mod == 3, both operands are registers
|
||||
if (mod == 3)
|
||||
{
|
||||
string rmRegName = ModRMDecoder.GetRegisterName(rm, 8);
|
||||
instruction.Operands = $"{rmRegName}, {regName}";
|
||||
}
|
||||
else // Memory operand
|
||||
{
|
||||
instruction.Operands = $"byte ptr {memOperand}, {regName}";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -215,6 +215,15 @@ public class InstructionDecoder
|
||||
return _prefixDecoder.HasRepPrefix();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the instruction has an operand size override prefix (0x66)
|
||||
/// </summary>
|
||||
/// <returns>True if the instruction has an operand size override prefix</returns>
|
||||
public bool HasOperandSizeOverridePrefix()
|
||||
{
|
||||
return _prefixDecoder.HasOperandSizePrefix();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte from the buffer and advances the position
|
||||
/// </summary>
|
||||
|
@ -188,6 +188,6 @@ public static class OpcodeMap
|
||||
/// <returns>The mnemonic</returns>
|
||||
public static string GetMnemonic(byte opcode)
|
||||
{
|
||||
return OneByteOpcodes[opcode];
|
||||
return "NO HANDLER: " + OneByteOpcodes[opcode];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,24 @@
|
||||
namespace X86DisassemblerTests.InstructionTests;
|
||||
using X86Disassembler.X86;
|
||||
|
||||
namespace X86DisassemblerTests.InstructionTests;
|
||||
|
||||
public class SubRm8Imm8Tests
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public void SubRm8Imm8_Decodes_Correctly()
|
||||
{
|
||||
// Arrange
|
||||
// SUB BL, 0x42
|
||||
byte[] codeBuffer = new byte[] { 0x80, 0xeb, 0x42 };
|
||||
var decoder = new Disassembler(codeBuffer, 0x1000);
|
||||
|
||||
// Act
|
||||
var instructions = decoder.Disassemble();
|
||||
|
||||
// Assert
|
||||
Assert.Single(instructions);
|
||||
Assert.NotNull(instructions[0]);
|
||||
Assert.Equal("sub", instructions[0].Mnemonic);
|
||||
Assert.Equal("bl, 0x42", instructions[0].Operands);
|
||||
}
|
||||
}
|
@ -6,26 +6,26 @@ RawBytes;Instructions
|
||||
90;[{ "Mnemonic": "nop", "Operands": "" }]
|
||||
|
||||
# Multi-byte NOP instructions (used for alignment)
|
||||
# 2-byte NOP
|
||||
# 2-byte NOP (xchg AX, AX)
|
||||
6690;[{ "Mnemonic": "nop", "Operands": "" }]
|
||||
|
||||
# 3-byte NOP (XCHG EAX, EAX)
|
||||
0F1F00;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax]" }]
|
||||
|
||||
# 4-byte NOP
|
||||
0F1F4000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+0x00]" }]
|
||||
0F1F4000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax]" }]
|
||||
|
||||
# 5-byte NOP
|
||||
0F1F440000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }]
|
||||
0F1F440000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax*1]" }]
|
||||
|
||||
# 6-byte NOP
|
||||
660F1F440000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }]
|
||||
660F1F440000;[{ "Mnemonic": "nop", "Operands": "word ptr [eax+eax*1]" }]
|
||||
|
||||
# 7-byte NOP
|
||||
0F1F8000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+0x00000000]" }]
|
||||
0F1F8000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax]" }]
|
||||
|
||||
# 8-byte NOP
|
||||
0F1F840000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }]
|
||||
0F1F840000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax*1]" }]
|
||||
|
||||
# 9-byte NOP
|
||||
660F1F840000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }]
|
||||
660F1F840000000000;[{ "Mnemonic": "nop", "Operands": "word ptr [eax+eax*1]" }]
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 7.
|
Loading…
x
Reference in New Issue
Block a user