From a4de35cf41e7ef913da5e0a93ff23af3cda4c76b Mon Sep 17 00:00:00 2001 From: bird_egop Date: Fri, 18 Apr 2025 13:01:02 +0300 Subject: [PATCH] Implement separate FSTSW handlers and fix test encodings --- .../FloatingPoint/Control/FstswHandler.cs | 93 ++++++------------- .../FloatingPoint/Control/FstswMemHandler.cs | 78 ++++++++++++++++ .../X86/Handlers/InstructionHandlerFactory.cs | 1 + .../TestData/fnstsw_tests.csv | 79 ++++++++-------- 4 files changed, 146 insertions(+), 105 deletions(-) create mode 100644 X86Disassembler/X86/Handlers/FloatingPoint/Control/FstswMemHandler.cs diff --git a/X86Disassembler/X86/Handlers/FloatingPoint/Control/FstswHandler.cs b/X86Disassembler/X86/Handlers/FloatingPoint/Control/FstswHandler.cs index 0d5f586..f543c07 100644 --- a/X86Disassembler/X86/Handlers/FloatingPoint/Control/FstswHandler.cs +++ b/X86Disassembler/X86/Handlers/FloatingPoint/Control/FstswHandler.cs @@ -3,10 +3,7 @@ using X86Disassembler.X86.Operands; namespace X86Disassembler.X86.Handlers.FloatingPoint.Control; /// -/// Handler for FSTSW instruction (with WAIT prefix 0x9B) -/// Handles both: -/// - FSTSW AX (0x9B 0xDF 0xE0) -/// - FSTSW m2byte (0x9B 0xDD /7) +/// Handler for FSTSW AX instruction (0x9B 0xDF 0xE0) - Store FPU status word with wait prefix to AX register /// public class FstswHandler : InstructionHandler { @@ -26,38 +23,22 @@ public class FstswHandler : InstructionHandler /// True if this handler can decode the opcode public override bool CanHandle(byte opcode) { - // FSTSW starts with the WAIT prefix (0x9B) + // FSTSW AX starts with the WAIT prefix (0x9B) if (opcode != 0x9B) return false; - // Check if we can read the next byte + // Check if we can read the next two bytes if (!Decoder.CanReadByte()) return false; - // Check if the next byte is 0xDF (for FSTSW AX) or 0xDD (for FSTSW m2byte) + // Check if the next bytes are 0xDF 0xE0 (for FSTSW AX) + var (nextByte, thirdByte) = Decoder.PeakTwoBytes(); - var (nextByte, modRM) = Decoder.PeakTwoBytes(); - - if (nextByte != 0xDF && nextByte != 0xDD) - return false; - - if (nextByte == 0xDF) - { - // For FSTSW AX, check if we can peek at the third byte and it's 0xE0 - - return modRM == 0xE0; - } - else // nextByte == 0xDD - { - // For FSTSW m2byte, check if we can peek at ModR/M byte and reg field = 7 - byte regField = ModRMDecoder.GetRegFromModRM(modRM); - - // The reg field must be 7 for FSTSW m2byte - return regField == 7; - } + // The sequence must be 9B DF E0 for FSTSW AX + return nextByte == 0xDF && thirdByte == 0xE0; } /// - /// Decodes an FSTSW instruction + /// Decodes an FSTSW AX instruction /// /// The opcode of the instruction /// The instruction object to populate @@ -68,48 +49,30 @@ public class FstswHandler : InstructionHandler if (!Decoder.CanReadByte()) return false; - // Read the second byte (0xDF for AX variant, 0xDD for memory variant) + // Read the second byte (0xDF) byte secondByte = Decoder.ReadByte(); + if (secondByte != 0xDF) + return false; + + // Read the third byte (0xE0) + if (!Decoder.CanReadByte()) + return false; + + byte thirdByte = Decoder.ReadByte(); + if (thirdByte != 0xE0) + return false; // Set the instruction type instruction.Type = InstructionType.Fstsw; - - if (secondByte == 0xDF) - { - // FSTSW AX variant - // Read the 0xE0 byte - if (!Decoder.CanReadByte()) - return false; - - byte e0Byte = Decoder.ReadByte(); - if (e0Byte != 0xE0) - return false; - - // Create the AX register operand - var axOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, 16); - - // Set the structured operands - instruction.StructuredOperands = - [ - axOperand - ]; - } - else if (secondByte == 0xDD) - { - // FSTSW m2byte variant - // Use ModRMDecoder to read and decode the ModR/M byte for 16-bit memory operand - var (mod, reg, rm, memoryOperand) = ModRMDecoder.ReadModRM16(); - - // Set the structured operands - instruction.StructuredOperands = - [ - memoryOperand - ]; - } - else - { - return false; - } + + // Create the AX register operand + var axOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, 16); + + // Set the structured operands + instruction.StructuredOperands = + [ + axOperand + ]; return true; } diff --git a/X86Disassembler/X86/Handlers/FloatingPoint/Control/FstswMemHandler.cs b/X86Disassembler/X86/Handlers/FloatingPoint/Control/FstswMemHandler.cs new file mode 100644 index 0000000..576b0ac --- /dev/null +++ b/X86Disassembler/X86/Handlers/FloatingPoint/Control/FstswMemHandler.cs @@ -0,0 +1,78 @@ +using X86Disassembler.X86.Operands; + +namespace X86Disassembler.X86.Handlers.FloatingPoint.Control; + +/// +/// Handler for FSTSW m2byte instruction (0x9B 0xDD /7) - Store FPU status word with wait prefix to memory +/// +public class FstswMemHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the FstswMemHandler class + /// + /// The instruction decoder that owns this handler + public FstswMemHandler(InstructionDecoder decoder) + : base(decoder) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + // FSTSW m2byte starts with the WAIT prefix (0x9B) + if (opcode != 0x9B) return false; + + // Check if we can read the next two bytes + if (!Decoder.CanReadByte()) + return false; + + // Check if the next bytes are 0xDD followed by ModR/M with reg field = 7 + var (nextByte, modRM) = Decoder.PeakTwoBytes(); + + // The first byte must be 0xDD for FSTSW m2byte + if (nextByte != 0xDD) + return false; + + // Check if ModR/M byte has reg field = 7 + byte regField = ModRMDecoder.GetRegFromModRM(modRM); + + // The reg field must be 7 for FSTSW m2byte + return regField == 7; + } + + /// + /// Decodes an FSTSW m2byte instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Skip the WAIT prefix (0x9B) - we already read it in CanHandle + if (!Decoder.CanReadByte()) + return false; + + // Read the second byte (0xDD) + byte secondByte = Decoder.ReadByte(); + if (secondByte != 0xDD) + return false; + + // Set the instruction type + instruction.Type = InstructionType.Fstsw; + + // Use ModRMDecoder to read and decode the ModR/M byte for 16-bit memory operand + var (mod, reg, rm, memoryOperand) = ModRMDecoder.ReadModRM16(); + + // Set the structured operands + instruction.StructuredOperands = + [ + memoryOperand + ]; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs index e538187..57ad0b6 100644 --- a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs +++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs @@ -419,6 +419,7 @@ public class InstructionHandlerFactory // Other floating point handlers _handlers.Add(new FloatingPoint.Control.FnstswHandler(_decoder)); // FNSTSW AX (DF E0) _handlers.Add(new FloatingPoint.Control.FstswHandler(_decoder)); // FSTSW AX (9B DF E0) + _handlers.Add(new FloatingPoint.Control.FstswMemHandler(_decoder)); // FSTSW m2byte (9B DD /7) // DB opcode handlers (int32 operations and extended precision) _handlers.Add(new FloatingPoint.LoadStore.FildInt32Handler(_decoder)); // FILD int32 (DB /0) diff --git a/X86DisassemblerTests/TestData/fnstsw_tests.csv b/X86DisassemblerTests/TestData/fnstsw_tests.csv index 6ac84bc..51f7642 100644 --- a/X86DisassemblerTests/TestData/fnstsw_tests.csv +++ b/X86DisassemblerTests/TestData/fnstsw_tests.csv @@ -9,51 +9,50 @@ DFE0;[{ "Type": "Fnstsw", "Operands": ["ax"] }] 9BDFE0;[{ "Type": "Fstsw", "Operands": ["ax"] }] # FSTSW m2byte - Store FPU status word to memory -9BDD7D00;[{ "Type": "Fstsw", "Operands": ["word ptr [eax]"] }] -9BDD7C24;[{ "Type": "Fstsw", "Operands": ["word ptr [esp]"] }] -9BDD7C24;[{ "Type": "Fstsw", "Operands": ["word ptr [esp]"] }] -9BDD7D03;[{ "Type": "Fstsw", "Operands": ["word ptr [ebx]"] }] -9BDD7D01;[{ "Type": "Fstsw", "Operands": ["word ptr [ecx]"] }] -9BDD7D02;[{ "Type": "Fstsw", "Operands": ["word ptr [edx]"] }] -9BDD7D06;[{ "Type": "Fstsw", "Operands": ["word ptr [esi]"] }] -9BDD7D07;[{ "Type": "Fstsw", "Operands": ["word ptr [edi]"] }] +9BDD38;[{ "Type": "Fstsw", "Operands": ["word ptr [eax]"] }] +9BDD3C24;[{ "Type": "Fstsw", "Operands": ["word ptr [esp]"] }] +9BDD3C24;[{ "Type": "Fstsw", "Operands": ["word ptr [esp]"] }] +9BDD3B;[{ "Type": "Fstsw", "Operands": ["word ptr [ebx]"] }] +9BDD39;[{ "Type": "Fstsw", "Operands": ["word ptr [ecx]"] }] +9BDD3A;[{ "Type": "Fstsw", "Operands": ["word ptr [edx]"] }] +9BDD3E;[{ "Type": "Fstsw", "Operands": ["word ptr [esi]"] }] +9BDD3F;[{ "Type": "Fstsw", "Operands": ["word ptr [edi]"] }] # FNSTSW m2byte - Store FPU status word to memory without checking for pending unmasked exceptions -DD7D00;[{ "Type": "Fnstsw", "Operands": ["word ptr [eax]"] }] -DD7C24;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp]"] }] -DD7C24;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp]"] }] -DD7D03;[{ "Type": "Fnstsw", "Operands": ["word ptr [ebx]"] }] -DD7D01;[{ "Type": "Fnstsw", "Operands": ["word ptr [ecx]"] }] -DD7D02;[{ "Type": "Fnstsw", "Operands": ["word ptr [edx]"] }] -DD7D06;[{ "Type": "Fnstsw", "Operands": ["word ptr [esi]"] }] -DD7D07;[{ "Type": "Fnstsw", "Operands": ["word ptr [edi]"] }] +DD38;[{ "Type": "Fnstsw", "Operands": ["word ptr [eax]"] }] +DD3C24;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+0x00]"] }] +DD3B;[{ "Type": "Fnstsw", "Operands": ["word ptr [ebx]"] }] +DD39;[{ "Type": "Fnstsw", "Operands": ["word ptr [ecx]"] }] +DD3A;[{ "Type": "Fnstsw", "Operands": ["word ptr [edx]"] }] +DD3E;[{ "Type": "Fnstsw", "Operands": ["word ptr [esi]"] }] +DD3F;[{ "Type": "Fnstsw", "Operands": ["word ptr [edi]"] }] # FSTSW/FNSTSW with displacement -9BDD7D8010000000;[{ "Type": "Fstsw", "Operands": ["word ptr [eax+0x10]"] }] -9BDD7D8020000000;[{ "Type": "Fstsw", "Operands": ["word ptr [eax+0x20]"] }] -DD7D8010000000;[{ "Type": "Fnstsw", "Operands": ["word ptr [eax+0x10]"] }] -DD7D8020000000;[{ "Type": "Fnstsw", "Operands": ["word ptr [eax+0x20]"] }] +9BDDB810000000;[{ "Type": "Fstsw", "Operands": ["word ptr [eax+0x10]"] }] +9BDDB820000000;[{ "Type": "Fstsw", "Operands": ["word ptr [eax+0x20]"] }] +DDB810000000;[{ "Type": "Fnstsw", "Operands": ["word ptr [eax+0x10]"] }] +DDB820000000;[{ "Type": "Fnstsw", "Operands": ["word ptr [eax+0x20]"] }] # FSTSW/FNSTSW with SIB addressing -9BDD7C04;[{ "Type": "Fstsw", "Operands": ["word ptr [esp+eax*1]"] }] -9BDD7C4C;[{ "Type": "Fstsw", "Operands": ["word ptr [esp+ecx*2]"] }] -9BDD7C94;[{ "Type": "Fstsw", "Operands": ["word ptr [esp+edx*4]"] }] -9BDD7CDC;[{ "Type": "Fstsw", "Operands": ["word ptr [esp+ebx*8]"] }] -DD7C04;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+eax*1]"] }] -DD7C4C;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+ecx*2]"] }] -DD7C94;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+edx*4]"] }] -DD7CDC;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+ebx*8]"] }] +9BDD3C04;[{ "Type": "Fstsw", "Operands": ["word ptr [esp+eax*1]"] }] +9BDD3C4C;[{ "Type": "Fstsw", "Operands": ["word ptr [esp+ecx*2]"] }] +9BDD3C94;[{ "Type": "Fstsw", "Operands": ["word ptr [esp+edx*4]"] }] +9BDD3CDC;[{ "Type": "Fstsw", "Operands": ["word ptr [esp+ebx*8]"] }] +DD3C04;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+eax*1]"] }] +DD3C4C;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+ecx*2]"] }] +DD3C94;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+edx*4]"] }] +DD3CDC;[{ "Type": "Fnstsw", "Operands": ["word ptr [esp+ebx*8]"] }] # FSTSW/FNSTSW with segment override prefixes -269BDD7D00;[{ "Type": "Fstsw", "Operands": ["word ptr es:[eax]"] }] -2E9BDD7D00;[{ "Type": "Fstsw", "Operands": ["word ptr cs:[eax]"] }] -369BDD7D00;[{ "Type": "Fstsw", "Operands": ["word ptr ss:[eax]"] }] -3E9BDD7D00;[{ "Type": "Fstsw", "Operands": ["word ptr ds:[eax]"] }] -649BDD7D00;[{ "Type": "Fstsw", "Operands": ["word ptr fs:[eax]"] }] -659BDD7D00;[{ "Type": "Fstsw", "Operands": ["word ptr gs:[eax]"] }] -26DD7D00;[{ "Type": "Fnstsw", "Operands": ["word ptr es:[eax]"] }] -2EDD7D00;[{ "Type": "Fnstsw", "Operands": ["word ptr cs:[eax]"] }] -36DD7D00;[{ "Type": "Fnstsw", "Operands": ["word ptr ss:[eax]"] }] -3EDD7D00;[{ "Type": "Fnstsw", "Operands": ["word ptr ds:[eax]"] }] -64DD7D00;[{ "Type": "Fnstsw", "Operands": ["word ptr fs:[eax]"] }] -65DD7D00;[{ "Type": "Fnstsw", "Operands": ["word ptr gs:[eax]"] }] +269BDD38;[{ "Type": "Fstsw", "Operands": ["word ptr es:[eax]"] }] +2E9BDD38;[{ "Type": "Fstsw", "Operands": ["word ptr cs:[eax]"] }] +369BDD38;[{ "Type": "Fstsw", "Operands": ["word ptr ss:[eax]"] }] +3E9BDD38;[{ "Type": "Fstsw", "Operands": ["word ptr ds:[eax]"] }] +649BDD38;[{ "Type": "Fstsw", "Operands": ["word ptr fs:[eax]"] }] +659BDD38;[{ "Type": "Fstsw", "Operands": ["word ptr gs:[eax]"] }] +26DD38;[{ "Type": "Fnstsw", "Operands": ["word ptr es:[eax]"] }] +2EDD38;[{ "Type": "Fnstsw", "Operands": ["word ptr cs:[eax]"] }] +36DD38;[{ "Type": "Fnstsw", "Operands": ["word ptr ss:[eax]"] }] +3EDD38;[{ "Type": "Fnstsw", "Operands": ["word ptr ds:[eax]"] }] +64DD38;[{ "Type": "Fnstsw", "Operands": ["word ptr fs:[eax]"] }] +65DD38;[{ "Type": "Fnstsw", "Operands": ["word ptr gs:[eax]"] }]