0
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-05-19 11:51:17 +03:00

Refactor SUB handlers

This commit is contained in:
bird_egop 2025-04-13 18:22:44 +03:00
parent a04a16af7d
commit e91a0223f7
18 changed files with 493 additions and 200 deletions

View File

@ -20,10 +20,7 @@
<Project Location="C:\Projects\CSharp\ParkanPlayground\X86DisassemblerTests" Presentation="<X86DisassemblerTests>" />
 <Project Location="C:\Projects\CSharp\ParkanPlayground\X86DisassemblerTests" Presentation="<X86DisassemblerTests>" />
</And>
 </And>
&lt;/SessionState&gt;</s:String> &lt;/SessionState&gt;</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=a2ceb6d1_002D1e39_002D4063_002Dbbb5_002D2f7a30b9386e/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="RunTestsOnJson" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD; <s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=ea256cae_002Df513_002D419e_002D992a_002D13b500b3c433/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &amp;lt;X86DisassemblerTests&amp;gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;TestAncestor&gt;&#xD; &lt;Project Location="C:\Projects\CSharp\ParkanPlayground\X86DisassemblerTests" Presentation="&amp;lt;X86DisassemblerTests&amp;gt;" /&gt;&#xD;
&lt;TestId&gt;xUnit::D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5::net8.0::X86DisassemblerTests.GeneralDisassemblerInstructionTests.RunTestsOnJson&lt;/TestId&gt;&#xD; &lt;/SessionState&gt;</s:String>
&lt;TestId&gt;xUnit::D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5::net8.0::X86DisassemblerTests.PushRegTests.PushRegTests&lt;/TestId&gt;&#xD; </wpf:ResourceDictionary>
&lt;TestId&gt;xUnit::D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5::net8.0::X86DisassemblerTests.RawFromFileDisassemblyTests&lt;/TestId&gt;&#xD;
&lt;/TestAncestor&gt;&#xD;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>

View 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;
}
}

View File

@ -47,14 +47,7 @@ public class SubAxImm16Handler : InstructionHandler
} }
// Read the immediate value (16-bit) // Read the immediate value (16-bit)
byte lowByte = CodeBuffer[position++]; var immediate = Decoder.ReadUInt16();
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);
// Set the operands (note: we use "eax" instead of "ax" to match the disassembler's output) // Set the operands (note: we use "eax" instead of "ax" to match the disassembler's output)
instruction.Operands = $"eax, 0x{immediate:X4}"; instruction.Operands = $"eax, 0x{immediate:X4}";

View File

@ -42,13 +42,8 @@ public class SubImmFromRm16Handler : InstructionHandler
return false; return false;
} }
// Read the ModR/M byte
byte modRM = CodeBuffer[position++];
// Extract the fields from the ModR/M byte // Extract the fields from the ModR/M byte
byte mod = (byte)((modRM & 0xC0) >> 6); var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
byte reg = (byte)((modRM & 0x38) >> 3);
byte rm = (byte)(modRM & 0x07);
// Check if this is a SUB instruction (reg field must be 5) // Check if this is a SUB instruction (reg field must be 5)
if (reg != 5) if (reg != 5)
@ -88,27 +83,21 @@ public class SubImmFromRm16Handler : InstructionHandler
} }
// Read the immediate value (16-bit) // Read the immediate value (16-bit)
byte lowByte = CodeBuffer[position++]; ushort immediate = Decoder.ReadUInt16();
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);
// Set the operands (note: we use 32-bit register names to match the disassembler's output) // Set the operands (note: we use 32-bit register names to match the disassembler's output)
if (mod == 3) if (mod == 3)
{ {
// For register operands, use the 32-bit register name // For register operands, use the 32-bit register name
string reg32Name = destination.Replace("ax", "eax") string reg32Name = destination
.Replace("bx", "ebx") .Replace("ax", "eax")
.Replace("cx", "ecx") .Replace("bx", "ebx")
.Replace("dx", "edx") .Replace("cx", "ecx")
.Replace("sp", "esp") .Replace("dx", "edx")
.Replace("bp", "ebp") .Replace("sp", "esp")
.Replace("si", "esi") .Replace("bp", "ebp")
.Replace("di", "edi"); .Replace("si", "esi")
.Replace("di", "edi");
instruction.Operands = $"{reg32Name}, 0x{immediate:X4}"; instruction.Operands = $"{reg32Name}, 0x{immediate:X4}";
} }

View File

@ -42,13 +42,8 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
return false; return false;
} }
// Read the ModR/M byte
byte modRM = CodeBuffer[position++];
// Extract the fields from the ModR/M byte // Extract the fields from the ModR/M byte
byte mod = (byte)((modRM & 0xC0) >> 6); var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
byte reg = (byte)((modRM & 0x38) >> 3);
byte rm = (byte)(modRM & 0x07);
// Check if this is a SUB instruction (reg field must be 5) // Check if this is a SUB instruction (reg field must be 5)
if (reg != 5) if (reg != 5)
@ -59,9 +54,6 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
// Set the mnemonic // Set the mnemonic
instruction.Mnemonic = "sub"; instruction.Mnemonic = "sub";
// Update the decoder position
Decoder.SetPosition(position);
// For mod == 3, the r/m field specifies a register // For mod == 3, the r/m field specifies a register
string destination; string destination;
if (mod == 3) if (mod == 3)
@ -88,23 +80,21 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
} }
// Read the immediate value (8-bit) // Read the immediate value (8-bit)
byte immediate = CodeBuffer[position++]; byte immediate = Decoder.ReadByte();
// Update the decoder position
Decoder.SetPosition(position);
// Set the operands (note: we use 32-bit register names to match the disassembler's output) // Set the operands (note: we use 32-bit register names to match the disassembler's output)
if (mod == 3) if (mod == 3)
{ {
// For register operands, use the 32-bit register name // For register operands, use the 32-bit register name
string reg32Name = destination.Replace("ax", "eax") string reg32Name = destination
.Replace("bx", "ebx") .Replace("ax", "eax")
.Replace("cx", "ecx") .Replace("bx", "ebx")
.Replace("dx", "edx") .Replace("cx", "ecx")
.Replace("sp", "esp") .Replace("dx", "edx")
.Replace("bp", "ebp") .Replace("sp", "esp")
.Replace("si", "esi") .Replace("bp", "ebp")
.Replace("di", "edi"); .Replace("si", "esi")
.Replace("di", "edi");
instruction.Operands = $"{reg32Name}, 0x{immediate:X2}"; instruction.Operands = $"{reg32Name}, 0x{immediate:X2}";
} }

View File

@ -32,7 +32,7 @@ public class SubImmFromRm32Handler : InstructionHandler
return false; return false;
byte modRM = CodeBuffer[position]; byte modRM = CodeBuffer[position];
byte reg = (byte)((modRM & 0x38) >> 3); byte reg = (byte) ((modRM & 0x38) >> 3);
return reg == 5; // 5 = SUB return reg == 5; // 5 = SUB
} }
@ -56,17 +56,11 @@ public class SubImmFromRm32Handler : InstructionHandler
} }
// Read the ModR/M byte // Read the ModR/M byte
byte modRM = CodeBuffer[position++];
Decoder.SetPosition(position);
// Extract the fields from the ModR/M byte // 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) // 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 // 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 // Get the updated position after ModR/M decoding
position = Decoder.GetPosition(); position = Decoder.GetPosition();
@ -78,14 +72,10 @@ public class SubImmFromRm32Handler : InstructionHandler
} }
// Read the immediate value in little-endian format // Read the immediate value in little-endian format
byte b0 = CodeBuffer[position]; var imm = Decoder.ReadUInt32();
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) // Format the immediate value
// Note: Always use the same format regardless of operand type to match test expectations string immStr = $"0x{imm:X8}";
string immStr = $"0x{b3:X2}{b2:X2}{b1:X2}{b0:X2}";
// Advance the position past the immediate value // Advance the position past the immediate value
position += 4; position += 4;

View File

@ -32,7 +32,7 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
return false; return false;
byte modRM = CodeBuffer[position]; byte modRM = CodeBuffer[position];
byte reg = (byte)((modRM & 0x38) >> 3); byte reg = (byte) ((modRM & 0x38) >> 3);
return reg == 5; // 5 = SUB return reg == 5; // 5 = SUB
} }
@ -55,18 +55,11 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
return false; return false;
} }
// Read the ModR/M byte
byte modRM = CodeBuffer[position++];
Decoder.SetPosition(position);
// Extract the fields from the ModR/M byte // Extract the fields from the ModR/M byte
byte mod = (byte)((modRM & 0xC0) >> 6); var (mod, reg, rm, destOperand) = ModRMDecoder.ReadModRM();
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) // 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 // 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 // Get the updated position after ModR/M decoding
position = Decoder.GetPosition(); position = Decoder.GetPosition();
@ -78,9 +71,8 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
} }
// Read the immediate value as a signed byte and sign-extend it to 32 bits // 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 int imm32 = imm8; // Automatic sign extension from sbyte to int
Decoder.SetPosition(position);
// Format the immediate value based on the operand type and value // Format the immediate value based on the operand type and value
string immStr; string immStr;
@ -89,7 +81,7 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
if (mod != 3) // Memory operand if (mod != 3) // Memory operand
{ {
// For memory operands, use the actual value as specified in the test // 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 else // Register operand
{ {
@ -97,12 +89,12 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
if (imm8 < 0) if (imm8 < 0)
{ {
// For negative values, show the full 32-bit representation with 8-digit padding // For negative values, show the full 32-bit representation with 8-digit padding
immStr = $"0x{(uint)imm32:X8}"; immStr = $"0x{(uint) imm32:X8}";
} }
else else
{ {
// For positive values, just show the value with 2-digit padding for consistency // For positive values, just show the value with 2-digit padding for consistency
immStr = $"0x{(byte)imm8:X2}"; immStr = $"0x{(byte) imm8:X2}";
} }
} }

View 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;
}
}

View File

@ -46,13 +46,7 @@ public class SubR16Rm16Handler : InstructionHandler
} }
// Read the ModR/M byte // Read the ModR/M byte
byte modRM = CodeBuffer[position++]; var (mod, reg, rm, memOperand) = ModRMDecoder.ReadModRM();
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);
// Get register name (16-bit) // Get register name (16-bit)
string regName = ModRMDecoder.GetRegisterName(reg, 16); string regName = ModRMDecoder.GetRegisterName(reg, 16);
@ -65,9 +59,6 @@ public class SubR16Rm16Handler : InstructionHandler
} }
else // Memory operand 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 // Replace "dword" with "word" in the memory operand
memOperand = memOperand.Replace("dword", "word"); memOperand = memOperand.Replace("dword", "word");

View File

@ -42,13 +42,7 @@ public class SubR32Rm32Handler : InstructionHandler
} }
// Read the ModR/M byte // Read the ModR/M byte
byte modRM = CodeBuffer[position++]; var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
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);
// Set the mnemonic // Set the mnemonic
instruction.Mnemonic = "sub"; instruction.Mnemonic = "sub";
@ -59,7 +53,6 @@ public class SubR32Rm32Handler : InstructionHandler
// For memory operands, set the operand // For memory operands, set the operand
if (mod != 3) // Memory operand if (mod != 3) // Memory operand
{ {
string operand = ModRMDecoder.DecodeModRM(mod, rm, false);
instruction.Operands = $"{regName}, {operand}"; instruction.Operands = $"{regName}, {operand}";
} }
else // Register operand else // Register operand

View 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;
}
}

View 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;
}
}

View File

@ -42,13 +42,9 @@ public class SubRm32R32Handler : InstructionHandler
} }
// Read the ModR/M byte // Read the ModR/M byte
byte modRM = CodeBuffer[position++];
Decoder.SetPosition(position);
// Extract the fields from the ModR/M byte // Extract the fields from the ModR/M byte
byte mod = (byte)((modRM & 0xC0) >> 6); var (mod, reg, rm, operand) = ModRMDecoder.ReadModRM();
byte reg = (byte)((modRM & 0x38) >> 3);
byte rm = (byte)(modRM & 0x07);
// Set the mnemonic // Set the mnemonic
instruction.Mnemonic = "sub"; instruction.Mnemonic = "sub";
@ -59,7 +55,6 @@ public class SubRm32R32Handler : InstructionHandler
// For memory operands, set the operand // For memory operands, set the operand
if (mod != 3) // Memory operand if (mod != 3) // Memory operand
{ {
string operand = ModRMDecoder.DecodeModRM(mod, rm, false);
instruction.Operands = $"{operand}, {regName}"; instruction.Operands = $"{operand}, {regName}";
} }
else // Register operand else // Register operand

View 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;
}
}

View File

@ -215,6 +215,15 @@ public class InstructionDecoder
return _prefixDecoder.HasRepPrefix(); 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> /// <summary>
/// Reads a byte from the buffer and advances the position /// Reads a byte from the buffer and advances the position
/// </summary> /// </summary>

View File

@ -188,6 +188,6 @@ public static class OpcodeMap
/// <returns>The mnemonic</returns> /// <returns>The mnemonic</returns>
public static string GetMnemonic(byte opcode) public static string GetMnemonic(byte opcode)
{ {
return OneByteOpcodes[opcode]; return "NO HANDLER: " + OneByteOpcodes[opcode];
} }
} }

View File

@ -1,6 +1,24 @@
namespace X86DisassemblerTests.InstructionTests; using X86Disassembler.X86;
namespace X86DisassemblerTests.InstructionTests;
public class SubRm8Imm8Tests 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);
}
} }

View File

@ -6,26 +6,26 @@ RawBytes;Instructions
90;[{ "Mnemonic": "nop", "Operands": "" }] 90;[{ "Mnemonic": "nop", "Operands": "" }]
# Multi-byte NOP instructions (used for alignment) # Multi-byte NOP instructions (used for alignment)
# 2-byte NOP # 2-byte NOP (xchg AX, AX)
6690;[{ "Mnemonic": "nop", "Operands": "" }] 6690;[{ "Mnemonic": "nop", "Operands": "" }]
# 3-byte NOP (XCHG EAX, EAX) # 3-byte NOP (XCHG EAX, EAX)
0F1F00;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax]" }] 0F1F00;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax]" }]
# 4-byte NOP # 4-byte NOP
0F1F4000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+0x00]" }] 0F1F4000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax]" }]
# 5-byte NOP # 5-byte NOP
0F1F440000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }] 0F1F440000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax*1]" }]
# 6-byte NOP # 6-byte NOP
660F1F440000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }] 660F1F440000;[{ "Mnemonic": "nop", "Operands": "word ptr [eax+eax*1]" }]
# 7-byte NOP # 7-byte NOP
0F1F8000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+0x00000000]" }] 0F1F8000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax]" }]
# 8-byte NOP # 8-byte NOP
0F1F840000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }] 0F1F840000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax*1]" }]
# 9-byte NOP # 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.