0
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:
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>" />
</And>
&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;
&lt;TestAncestor&gt;&#xD;
&lt;TestId&gt;xUnit::D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5::net8.0::X86DisassemblerTests.GeneralDisassemblerInstructionTests.RunTestsOnJson&lt;/TestId&gt;&#xD;
&lt;TestId&gt;xUnit::D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5::net8.0::X86DisassemblerTests.PushRegTests.PushRegTests&lt;/TestId&gt;&#xD;
&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>
<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;Project Location="C:\Projects\CSharp\ParkanPlayground\X86DisassemblerTests" Presentation="&amp;lt;X86DisassemblerTests&amp;gt;" /&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)
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}";

View File

@ -42,13 +42,8 @@ public class SubImmFromRm16Handler : InstructionHandler
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)
@ -88,20 +83,14 @@ public class SubImmFromRm16Handler : InstructionHandler
}
// 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")
string reg32Name = destination
.Replace("ax", "eax")
.Replace("bx", "ebx")
.Replace("cx", "ecx")
.Replace("dx", "edx")

View File

@ -42,13 +42,8 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
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)
@ -59,9 +54,6 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
// 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)
@ -88,16 +80,14 @@ public class SubImmFromRm16SignExtendedHandler : InstructionHandler
}
// 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")
string reg32Name = destination
.Replace("ax", "eax")
.Replace("bx", "ebx")
.Replace("cx", "ecx")
.Replace("dx", "edx")

View File

@ -32,7 +32,7 @@ public class SubImmFromRm32Handler : InstructionHandler
return false;
byte modRM = CodeBuffer[position];
byte reg = (byte)((modRM & 0x38) >> 3);
byte reg = (byte) ((modRM & 0x38) >> 3);
return reg == 5; // 5 = SUB
}
@ -56,17 +56,11 @@ public class SubImmFromRm32Handler : 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); // 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();
@ -78,14 +72,10 @@ public class SubImmFromRm32Handler : InstructionHandler
}
// 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];
var imm = Decoder.ReadUInt32();
// 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}";
// Format the immediate value
string immStr = $"0x{imm:X8}";
// Advance the position past the immediate value
position += 4;

View File

@ -32,7 +32,7 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
return false;
byte modRM = CodeBuffer[position];
byte reg = (byte)((modRM & 0x38) >> 3);
byte reg = (byte) ((modRM & 0x38) >> 3);
return reg == 5; // 5 = SUB
}
@ -55,18 +55,11 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
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();
@ -78,9 +71,8 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
}
// 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;
@ -89,7 +81,7 @@ public class SubImmFromRm32SignExtendedHandler : InstructionHandler
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,12 +89,12 @@ 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}";
}
}

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
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);
@ -65,9 +59,6 @@ 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");

View File

@ -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

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
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

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();
}
/// <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>

View File

@ -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];
}
}

View File

@ -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);
}
}

View File

@ -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.