Skip to main content

zydis_rs/decoder/
instruction.rs

1//! Decoded instruction representation
2//!
3//! This module contains the main `DecodedInstruction` struct that holds
4//! all information about a decoded x86/x64 instruction.
5
6use super::opcode::OpcodeMap;
7use super::operand::Operand;
8use super::prefix::DecodedPrefixes;
9use crate::data::instruction_def::{InstructionCategory, IsaSet};
10use crate::isa::{InstructionAttributes, MachineMode, Mnemonic, Register};
11
12/// Maximum number of operands an instruction can have
13pub const MAX_OPERAND_COUNT: usize = 5;
14
15/// Stack pointer modification information
16///
17/// Describes how an instruction modifies the stack pointer (SP/ESP/RSP).
18/// This is useful for stack frame analysis and function prologue/epilogue detection.
19#[derive(Debug, Clone, Copy, Default)]
20pub struct StackPointerInfo {
21    /// Stack pointer offset change (positive = push, negative = pop)
22    /// For example, PUSH RAX sets this to +8, POP RBX sets this to -8
23    pub sp_offset: i64,
24    /// Whether this instruction modifies the stack pointer
25    pub modifies_sp: bool,
26    /// Whether this is a push operation
27    pub is_push: bool,
28    /// Whether this is a pop operation
29    pub is_pop: bool,
30    /// Whether this is a call instruction (pushes return address)
31    pub is_call: bool,
32    /// Whether this is a return instruction (pops return address)
33    pub is_ret: bool,
34    /// Size of pushed/popped data in bytes (0 if not applicable)
35    pub data_size: u8,
36}
37
38impl StackPointerInfo {
39    /// Create a new empty StackPointerInfo
40    #[must_use]
41    pub const fn new() -> Self {
42        Self {
43            sp_offset: 0,
44            modifies_sp: false,
45            is_push: false,
46            is_pop: false,
47            is_call: false,
48            is_ret: false,
49            data_size: 0,
50        }
51    }
52
53    /// Create StackPointerInfo for a push operation
54    #[must_use]
55    pub const fn push(data_size: u8) -> Self {
56        Self {
57            sp_offset: data_size as i64,
58            modifies_sp: true,
59            is_push: true,
60            is_pop: false,
61            is_call: false,
62            is_ret: false,
63            data_size,
64        }
65    }
66
67    /// Create StackPointerInfo for a pop operation
68    #[must_use]
69    pub const fn pop(data_size: u8) -> Self {
70        Self {
71            sp_offset: -(data_size as i64),
72            modifies_sp: true,
73            is_push: false,
74            is_pop: true,
75            is_call: false,
76            is_ret: false,
77            data_size,
78        }
79    }
80
81    /// Create StackPointerInfo for a call instruction
82    #[must_use]
83    pub const fn call(return_address_size: u8) -> Self {
84        Self {
85            sp_offset: return_address_size as i64,
86            modifies_sp: true,
87            is_push: false,
88            is_pop: false,
89            is_call: true,
90            is_ret: false,
91            data_size: return_address_size,
92        }
93    }
94
95    /// Create StackPointerInfo for a return instruction
96    #[must_use]
97    pub const fn ret(return_address_size: u8) -> Self {
98        Self {
99            sp_offset: -(return_address_size as i64),
100            modifies_sp: true,
101            is_push: false,
102            is_pop: false,
103            is_call: false,
104            is_ret: true,
105            data_size: return_address_size,
106        }
107    }
108
109    /// Create StackPointerInfo for ENTER instruction
110    /// ENTER allocates stack frame: push rbp, mov rbp rsp, sub rsp imm16
111    #[must_use]
112    pub const fn enter(frame_size: u16, nesting_level: u8, stack_width: u8) -> Self {
113        // ENTER pushes RBP and subtracts frame_size
114        // Nesting level affects additional pushes
115        let base_offset = stack_width as i64 + frame_size as i64;
116        let nesting_offset = if nesting_level > 0 {
117            (nesting_level as i64 - 1) * (stack_width as i64)
118        } else {
119            0
120        };
121        Self {
122            sp_offset: base_offset + nesting_offset,
123            modifies_sp: true,
124            is_push: false,
125            is_pop: false,
126            is_call: false,
127            is_ret: false,
128            data_size: stack_width,
129        }
130    }
131
132    /// Create StackPointerInfo for LEAVE instruction
133    /// LEAVE is equivalent to: mov rsp, rbp; pop rbp
134    #[must_use]
135    pub const fn leave(stack_width: u8) -> Self {
136        Self {
137            sp_offset: -(stack_width as i64),
138            modifies_sp: true,
139            is_push: false,
140            is_pop: true, // LEAVE effectively pops RBP
141            is_call: false,
142            is_ret: false,
143            data_size: stack_width,
144        }
145    }
146
147    /// Get the stack pointer offset
148    #[must_use]
149    pub const fn offset(&self) -> i64 {
150        self.sp_offset
151    }
152
153    /// Check if stack pointer is modified
154    #[must_use]
155    pub const fn is_stack_modifying(&self) -> bool {
156        self.modifies_sp
157    }
158
159    /// Check if this is a stack push operation
160    #[must_use]
161    pub const fn is_stack_push(&self) -> bool {
162        self.is_push
163    }
164
165    /// Check if this is a stack pop operation
166    #[must_use]
167    pub const fn is_stack_pop(&self) -> bool {
168        self.is_pop
169    }
170
171    /// Check if this is a call instruction
172    #[must_use]
173    pub const fn is_call_instruction(&self) -> bool {
174        self.is_call
175    }
176
177    /// Check if this is a return instruction
178    #[must_use]
179    pub const fn is_ret_instruction(&self) -> bool {
180        self.is_ret
181    }
182
183    /// Get the data size for stack operations
184    #[must_use]
185    pub const fn get_data_size(&self) -> u8 {
186        self.data_size
187    }
188}
189
190/// A decoded x86/x64 instruction.
191///
192/// This structure contains all information extracted from decoding a single
193/// instruction, including:
194/// - The mnemonic (e.g., MOV, ADD, JMP)
195/// - All operands (registers, memory, immediates)
196/// - Instruction attributes and flags
197/// - Raw bytes and length
198/// - Encoding details (prefixes, opcode, ModRM, etc.)
199#[derive(Debug, Clone)]
200pub struct DecodedInstruction {
201    /// The instruction mnemonic
202    pub mnemonic: Mnemonic,
203
204    /// Instruction category (data transfer, arithmetic, etc.)
205    pub category: InstructionCategory,
206
207    /// ISA set this instruction belongs to
208    pub isa_set: IsaSet,
209
210    /// Instruction attributes (flags describing properties)
211    pub attributes: InstructionAttributes,
212
213    /// Operands (up to MAX_OPERAND_COUNT)
214    pub operands: [Operand; MAX_OPERAND_COUNT],
215
216    /// Number of valid operands
217    pub operand_count: u8,
218
219    /// Raw instruction bytes
220    pub bytes: [u8; 15],
221
222    /// Instruction length in bytes (1-15)
223    pub length: u8,
224
225    /// The address this instruction was decoded at
226    pub address: u64,
227
228    /// Prefix information
229    pub prefixes: DecodedPrefixes,
230
231    /// Primary opcode byte
232    pub opcode: u8,
233
234    /// Opcode map (Default, Map0F, Map0F38, Map0F3A)
235    pub opcode_map: OpcodeMap,
236
237    /// ModRM byte (if present)
238    pub modrm: Option<u8>,
239
240    /// SIB byte (if present)
241    pub sib: Option<u8>,
242
243    /// Displacement value (if present)
244    pub displacement: i64,
245
246    /// Displacement size in bytes
247    pub disp_size: u8,
248
249    /// Immediate value (if present)
250    pub immediate: u64,
251
252    /// Immediate size in bytes
253    pub imm_size: u8,
254
255    /// The machine mode used for decoding
256    pub machine_mode: MachineMode,
257
258    /// Stack width for this instruction
259    pub stack_width: u8,
260
261    /// Effective operand size (in bits)
262    pub operand_size: u16,
263
264    /// Effective address size (in bits)
265    pub address_size: u16,
266
267    /// CPU flags accessed by this instruction
268    pub cpu_flags: CpuFlags,
269
270    /// Stack pointer modification information
271    pub stack_pointer_info: StackPointerInfo,
272}
273
274impl DecodedInstruction {
275    /// Create a new empty/invalid instruction
276    #[must_use]
277    pub fn new() -> Self {
278        Self {
279            mnemonic: Mnemonic::Invalid,
280            category: InstructionCategory::Invalid,
281            isa_set: IsaSet::I86,
282            attributes: InstructionAttributes::empty(),
283            operands: [Operand::default(); MAX_OPERAND_COUNT],
284            operand_count: 0,
285            bytes: [0; 15],
286            length: 0,
287            address: 0,
288            prefixes: DecodedPrefixes::default(),
289            opcode: 0,
290            opcode_map: OpcodeMap::Default,
291            modrm: None,
292            sib: None,
293            displacement: 0,
294            disp_size: 0,
295            immediate: 0,
296            imm_size: 0,
297            machine_mode: MachineMode::Long64,
298            stack_width: 64,
299            operand_size: 32,
300            address_size: 64,
301            cpu_flags: CpuFlags::default(),
302            stack_pointer_info: StackPointerInfo::new(),
303        }
304    }
305
306    /// Get the instruction bytes as a slice
307    #[must_use]
308    pub fn raw_bytes(&self) -> &[u8] {
309        &self.bytes[..usize::from(self.length)]
310    }
311
312    /// Check if this is a valid instruction
313    #[must_use]
314    pub fn is_valid(&self) -> bool {
315        self.length > 0 && self.mnemonic != Mnemonic::Invalid
316    }
317
318    /// Check if the instruction has a ModRM byte
319    #[must_use]
320    pub const fn has_modrm(&self) -> bool {
321        self.modrm.is_some()
322    }
323
324    /// Check if the instruction has a SIB byte
325    #[must_use]
326    pub const fn has_sib(&self) -> bool {
327        self.sib.is_some()
328    }
329
330    /// Check if the instruction has a displacement
331    #[must_use]
332    pub const fn has_displacement(&self) -> bool {
333        self.disp_size > 0
334    }
335
336    /// Check if the instruction has an immediate
337    #[must_use]
338    pub const fn has_immediate(&self) -> bool {
339        self.imm_size > 0
340    }
341
342    /// Check if this is a branch instruction
343    #[must_use]
344    pub const fn is_branch(&self) -> bool {
345        self.attributes.contains(InstructionAttributes::IS_BRANCH)
346    }
347
348    /// Check if this is a conditional instruction
349    #[must_use]
350    pub const fn is_conditional(&self) -> bool {
351        self.attributes
352            .contains(InstructionAttributes::IS_CONDITIONAL)
353    }
354
355    /// Check if this is a privileged instruction
356    #[must_use]
357    pub const fn is_privileged(&self) -> bool {
358        self.attributes
359            .contains(InstructionAttributes::IS_PRIVILEGED)
360    }
361
362    /// Check if this instruction modifies the stack
363    #[must_use]
364    pub const fn is_stack_modifying(&self) -> bool {
365        self.attributes
366            .contains(InstructionAttributes::IS_STACK_MODIFYING)
367    }
368
369    /// Check if this is a relative branch/call
370    #[must_use]
371    pub const fn is_relative(&self) -> bool {
372        self.attributes.contains(InstructionAttributes::IS_RELATIVE)
373    }
374
375    /// Get the next instruction address (for flow analysis)
376    #[must_use]
377    pub const fn next_address(&self) -> u64 {
378        self.address.wrapping_add(self.length as u64)
379    }
380
381    /// Get the branch target address (if this is a relative branch)
382    #[must_use]
383    pub fn branch_target(&self) -> Option<u64> {
384        if self.is_relative() && self.is_branch() {
385            // Sign-extend displacement to 64-bit and add to next instruction address
386            let target = self.next_address().wrapping_add(self.displacement as u64);
387            Some(target)
388        } else {
389            None
390        }
391    }
392
393    /// Iterate over valid operands
394    pub fn operands(&self) -> impl Iterator<Item = &Operand> {
395        self.operands[..usize::from(self.operand_count)].iter()
396    }
397
398    /// Get a specific operand by index
399    #[must_use]
400    pub fn operand(&self, index: usize) -> Option<&Operand> {
401        if index < usize::from(self.operand_count) {
402            Some(&self.operands[index])
403        } else {
404            None
405        }
406    }
407
408    /// Set an operand
409    pub fn set_operand(&mut self, index: usize, operand: Operand) {
410        if index < MAX_OPERAND_COUNT {
411            self.operands[index] = operand;
412            if index >= usize::from(self.operand_count) {
413                self.operand_count = (index + 1) as u8;
414            }
415        }
416    }
417
418    // CPU Flags access methods
419
420    /// Get CPU flags read by this instruction
421    ///
422    /// Returns a bitmask of flags that are read by the instruction.
423    #[must_use]
424    pub const fn cpu_flags_read(&self) -> u64 {
425        self.cpu_flags.read
426    }
427
428    /// Get CPU flags written by this instruction
429    ///
430    /// Returns a bitmask of flags that are written by the instruction.
431    #[must_use]
432    pub const fn cpu_flags_written(&self) -> u64 {
433        self.cpu_flags.written
434    }
435
436    /// Get CPU flags modified by this instruction
437    ///
438    /// Returns a bitmask of flags that are both read and written by the instruction.
439    #[must_use]
440    pub const fn cpu_flags_modified(&self) -> u64 {
441        self.cpu_flags.modified
442    }
443
444    /// Get CPU flags set to undefined values by this instruction
445    ///
446    /// Returns a bitmask of flags that have undefined values after the instruction executes.
447    #[must_use]
448    pub const fn cpu_flags_undefined(&self) -> u64 {
449        self.cpu_flags.undefined
450    }
451
452    /// Get reference to the CPU flags structure
453    ///
454    /// Returns a reference to the complete CPU flags information.
455    #[must_use]
456    pub const fn get_cpu_flags(&self) -> &CpuFlags {
457        &self.cpu_flags
458    }
459
460    /// Check if the instruction reads any CPU flags
461    #[must_use]
462    pub const fn reads_any_cpu_flag(&self) -> bool {
463        self.cpu_flags.reads_any()
464    }
465
466    /// Check if the instruction writes any CPU flags
467    #[must_use]
468    pub const fn writes_any_cpu_flag(&self) -> bool {
469        self.cpu_flags.writes_any()
470    }
471
472    /// Check if the instruction modifies any CPU flags
473    #[must_use]
474    pub const fn modifies_any_cpu_flag(&self) -> bool {
475        self.cpu_flags.modifies_any()
476    }
477
478    /// Check if the instruction reads a specific CPU flag
479    #[must_use]
480    pub const fn reads_cpu_flag(&self, flag: CPUFlag) -> bool {
481        self.cpu_flags.reads_flag(flag)
482    }
483
484    /// Check if the instruction writes a specific CPU flag
485    #[must_use]
486    pub const fn writes_cpu_flag(&self, flag: CPUFlag) -> bool {
487        self.cpu_flags.writes_flag(flag)
488    }
489
490    /// Check if the instruction modifies a specific CPU flag
491    #[must_use]
492    pub const fn modifies_cpu_flag(&self, flag: CPUFlag) -> bool {
493        self.cpu_flags.modifies_flag(flag)
494    }
495
496    // Stack pointer access methods
497
498    /// Get stack pointer modification information
499    ///
500    /// Returns information about how this instruction modifies the stack pointer.
501    #[must_use]
502    pub const fn get_stack_pointer_info(&self) -> &StackPointerInfo {
503        &self.stack_pointer_info
504    }
505
506    /// Get stack pointer offset change
507    ///
508    /// Returns the change in stack pointer value (positive = push, negative = pop).
509    /// Returns 0 if the instruction doesn't modify the stack pointer.
510    #[must_use]
511    pub const fn stack_pointer_offset(&self) -> i64 {
512        self.stack_pointer_info.sp_offset
513    }
514
515    /// Check if this instruction modifies the stack pointer
516    #[must_use]
517    pub const fn modifies_stack_pointer(&self) -> bool {
518        self.stack_pointer_info.modifies_sp
519    }
520
521    /// Check if this is a stack push operation (PUSH instruction)
522    #[must_use]
523    pub const fn is_push(&self) -> bool {
524        self.stack_pointer_info.is_push
525    }
526
527    /// Check if this is a stack pop operation (POP instruction)
528    #[must_use]
529    pub const fn is_pop(&self) -> bool {
530        self.stack_pointer_info.is_pop
531    }
532
533    /// Check if this is a call instruction
534    #[must_use]
535    pub const fn is_call(&self) -> bool {
536        self.stack_pointer_info.is_call
537    }
538
539    /// Check if this is a return instruction
540    #[must_use]
541    pub const fn is_ret(&self) -> bool {
542        self.stack_pointer_info.is_ret
543    }
544
545    /// Set stack pointer info (used by decoder)
546    pub fn set_stack_pointer_info(&mut self, info: StackPointerInfo) {
547        self.stack_pointer_info = info;
548    }
549
550    // ============================================================================
551    // Phase 5: Helper API methods (Zydis compatibility)
552    // ============================================================================
553
554    /// Get the instruction category
555    ///
556    /// Returns the category of this instruction (e.g., DataTransfer, Arithmetic, ControlFlow).
557    #[must_use]
558    pub const fn category(&self) -> InstructionCategory {
559        self.category
560    }
561
562    /// Get the ISA set for this instruction
563    ///
564    /// Returns the instruction set extension required for this instruction
565    /// (e.g., SSE, AVX, AVX512, etc.)
566    #[must_use]
567    pub const fn isa_set(&self) -> IsaSet {
568        self.isa_set
569    }
570
571    /// Get the branch type for this instruction
572    ///
573    /// Returns `Some(BranchType)` if this is a branch instruction, `None` otherwise.
574    #[must_use]
575    pub fn branch_type(&self) -> Option<BranchType> {
576        if !self.is_branch() {
577            return None;
578        }
579
580        // Determine branch type based on displacement size and instruction attributes
581        if self.is_conditional() {
582            // Conditional branches (Jcc, LOOP, etc.)
583            if self.disp_size == 1 {
584                Some(BranchType::Short)
585            } else {
586                Some(BranchType::Near)
587            }
588        } else {
589            // Unconditional branches (JMP, CALL, RET)
590            match self.disp_size {
591                1 => Some(BranchType::Short),
592                2 | 4 => Some(BranchType::Near),
593                _ => {
594                    // Check for far branches (JMP FAR, CALL FAR)
595                    if self
596                        .attributes
597                        .contains(InstructionAttributes::IS_FAR_BRANCH)
598                    {
599                        Some(BranchType::Far)
600                    } else {
601                        Some(BranchType::Near)
602                    }
603                }
604            }
605        }
606    }
607
608    /// Get the exception class for this instruction
609    ///
610    /// Returns the exception class that defines which exceptions this instruction
611    /// can generate (primarily for AVX-512 and SIMD instructions).
612    #[must_use]
613    pub const fn exception_class(&self) -> ExceptionClass {
614        // Determine exception class based on ISA set and attributes
615        if self.isa_set.is_avx512() {
616            ExceptionClass::EVEX
617        } else if self.isa_set.is_avx() {
618            ExceptionClass::VEX
619        } else if self.isa_set.is_xop() {
620            ExceptionClass::XOP
621        } else if self.isa_set.is_sse() {
622            ExceptionClass::SSE
623        } else {
624            ExceptionClass::None
625        }
626    }
627
628    /// Calculate the absolute address for a relative operand
629    ///
630    /// This function computes the absolute target address for branch instructions
631    /// or memory references that use RIP-relative addressing.
632    ///
633    /// # Arguments
634    /// * `operand_index` - The index of the operand (0-based)
635    /// * `rip` - The address of the next instruction (instruction address + length)
636    ///
637    /// # Returns
638    /// * `Some(u64)` - The calculated absolute address
639    /// * `None` - If the operand is not a relative address or the index is invalid
640    #[must_use]
641    pub fn calc_absolute_address(&self, operand_index: usize, rip: u64) -> Option<u64> {
642        let operand = self.operand(operand_index)?;
643
644        match operand.kind {
645            crate::isa::OperandKind::RelativeOffset => {
646                // Get the signed displacement
647                let disp = operand.imm_signed()?;
648                // Calculate absolute address: rip + displacement
649                Some(rip.wrapping_add(disp as u64))
650            }
651            crate::isa::OperandKind::Memory => {
652                // Check for RIP-relative addressing
653                let mem = operand.mem()?;
654                if mem.base == Register::RIP {
655                    // RIP-relative: rip + displacement
656                    Some(rip.wrapping_add(mem.displacement as u64))
657                } else {
658                    None
659                }
660            }
661            _ => None,
662        }
663    }
664
665    /// Get the condition code for conditional instructions
666    ///
667    /// Returns the condition code (0-15) for conditional instructions like Jcc, SETcc, CMOVcc.
668    /// Returns `None` if this is not a conditional instruction.
669    #[must_use]
670    pub fn condition_code(&self) -> Option<ConditionCode> {
671        if !self.is_conditional() {
672            return None;
673        }
674
675        // Extract condition code from opcode
676        // For Jcc, the condition is encoded in the low 4 bits of the opcode
677        let opcode = self.opcode;
678
679        // Check for common conditional instruction patterns
680        match self.mnemonic {
681            // Jcc short (0x70-0x7F)
682            Mnemonic::JO
683            | Mnemonic::JNO
684            | Mnemonic::JB
685            | Mnemonic::JNB
686            | Mnemonic::JZ
687            | Mnemonic::JNZ
688            | Mnemonic::JBE
689            | Mnemonic::JA
690            | Mnemonic::JS
691            | Mnemonic::JNS
692            | Mnemonic::JP
693            | Mnemonic::JNP
694            | Mnemonic::JL
695            | Mnemonic::JGE
696            | Mnemonic::JLE
697            | Mnemonic::JG => {
698                let cc = if (0x70..=0x7F).contains(&opcode) {
699                    opcode - 0x70
700                } else if opcode == 0x0F {
701                    // Two-byte Jcc (0x0F 0x80-0x8F)
702                    // Need second byte
703                    (self.bytes[1].wrapping_sub(0x80)) & 0x0F
704                } else {
705                    return None;
706                };
707                ConditionCode::from_u8(cc)
708            }
709            // SETcc (0x0F 0x90-0x9F)
710            Mnemonic::SETO
711            | Mnemonic::SETNO
712            | Mnemonic::SETB
713            | Mnemonic::SETNB
714            | Mnemonic::SETZ
715            | Mnemonic::SETNZ
716            | Mnemonic::SETBE
717            | Mnemonic::SETNBE
718            | Mnemonic::SETS
719            | Mnemonic::SETNS
720            | Mnemonic::SETP
721            | Mnemonic::SETNP
722            | Mnemonic::SETL
723            | Mnemonic::SETGE
724            | Mnemonic::SETLE
725            | Mnemonic::SETG => {
726                let cc = if self.bytes.len() > 1 && self.bytes[1] >= 0x90 && self.bytes[1] <= 0x9F {
727                    (self.bytes[1] - 0x90) & 0x0F
728                } else {
729                    return None;
730                };
731                ConditionCode::from_u8(cc)
732            }
733            // CMOVcc (0x0F 0x40-0x4F)
734            Mnemonic::CMOVO
735            | Mnemonic::CMOVNO
736            | Mnemonic::CMOVB
737            | Mnemonic::CMOVNB
738            | Mnemonic::CMOVZ
739            | Mnemonic::CMOVNZ
740            | Mnemonic::CMOVBE
741            | Mnemonic::CMOVA
742            | Mnemonic::CMOVS
743            | Mnemonic::CMOVNS
744            | Mnemonic::CMOVP
745            | Mnemonic::CMOVNP
746            | Mnemonic::CMOVL
747            | Mnemonic::CMOVGE
748            | Mnemonic::CMOVLE
749            | Mnemonic::CMOVG => {
750                let cc = if self.bytes.len() > 1 && self.bytes[1] >= 0x40 && self.bytes[1] <= 0x4F {
751                    (self.bytes[1] - 0x40) & 0x0F
752                } else {
753                    return None;
754                };
755                ConditionCode::from_u8(cc)
756            }
757            _ => None,
758        }
759    }
760}
761
762/// Branch type enumeration
763///
764/// Defines the type of branch instruction.
765#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
766#[repr(u8)]
767#[derive(Default)]
768pub enum BranchType {
769    /// No branch type specified
770    #[default]
771    None,
772    /// Short branch - 8-bit relative offset (±128 bytes)
773    Short,
774    /// Near branch - 32-bit relative offset (±2GB)
775    Near,
776    /// Far branch - absolute address with segment selector
777    Far,
778}
779
780/// Exception class enumeration
781///
782/// Defines the exception class for SIMD instructions.
783#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
784#[repr(u8)]
785#[derive(Default)]
786pub enum ExceptionClass {
787    /// No exception class
788    #[default]
789    None,
790    /// SSE/SSE2 exception class
791    SSE,
792    /// AVX/AVX2 VEX-encoded exception class
793    VEX,
794    /// AVX-512 EVEX-encoded exception class
795    EVEX,
796    /// AMD XOP exception class
797    XOP,
798}
799
800/// Condition code enumeration
801///
802/// Defines the condition codes for conditional instructions (Jcc, SETcc, CMOVcc).
803#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
804#[repr(u8)]
805pub enum ConditionCode {
806    /// Overflow (OF=1)
807    O = 0,
808    /// No overflow (OF=0)
809    NO = 1,
810    /// Below/Carry (CF=1)
811    B = 2,
812    /// Not below/No carry (CF=0)
813    NB = 3,
814    /// Equal/Zero (ZF=1)
815    Z = 4,
816    /// Not equal/Not zero (ZF=0)
817    NZ = 5,
818    /// Below or equal (CF=1 or ZF=1)
819    BE = 6,
820    /// Above (CF=0 and ZF=0)
821    A = 7,
822    /// Sign (SF=1)
823    S = 8,
824    /// No sign (SF=0)
825    NS = 9,
826    /// Parity (PF=1)
827    P = 10,
828    /// No parity (PF=0)
829    NP = 11,
830    /// Less (SF!=OF)
831    L = 12,
832    /// Greater or equal (SF=OF)
833    GE = 13,
834    /// Less or equal (ZF=1 or SF!=OF)
835    LE = 14,
836    /// Greater (ZF=0 and SF=OF)
837    G = 15,
838}
839
840impl ConditionCode {
841    /// Create a ConditionCode from a u8 value
842    #[must_use]
843    pub const fn from_u8(value: u8) -> Option<Self> {
844        match value {
845            0 => Some(Self::O),
846            1 => Some(Self::NO),
847            2 => Some(Self::B),
848            3 => Some(Self::NB),
849            4 => Some(Self::Z),
850            5 => Some(Self::NZ),
851            6 => Some(Self::BE),
852            7 => Some(Self::A),
853            8 => Some(Self::S),
854            9 => Some(Self::NS),
855            10 => Some(Self::P),
856            11 => Some(Self::NP),
857            12 => Some(Self::L),
858            13 => Some(Self::GE),
859            14 => Some(Self::LE),
860            15 => Some(Self::G),
861            _ => None,
862        }
863    }
864
865    /// Get the condition code name
866    #[must_use]
867    pub const fn name(&self) -> &'static str {
868        match self {
869            Self::O => "O",
870            Self::NO => "NO",
871            Self::B => "B",
872            Self::NB => "NB",
873            Self::Z => "Z",
874            Self::NZ => "NZ",
875            Self::BE => "BE",
876            Self::A => "A",
877            Self::S => "S",
878            Self::NS => "NS",
879            Self::P => "P",
880            Self::NP => "NP",
881            Self::L => "L",
882            Self::GE => "GE",
883            Self::LE => "LE",
884            Self::G => "G",
885        }
886    }
887}
888
889impl Default for DecodedInstruction {
890    fn default() -> Self {
891        Self::new()
892    }
893}
894
895/// CPU flags information
896///
897/// Describes which CPU flags are read, written, or modified by an instruction.
898/// This matches the behavior of Zydis's CPU flags API.
899#[derive(Debug, Clone, Copy, Default)]
900pub struct CpuFlags {
901    /// Flags read by the instruction (as a bitmask)
902    pub read: u64,
903    /// Flags written by the instruction (as a bitmask)
904    pub written: u64,
905    /// Flags modified (read and written) by the instruction (as a bitmask)
906    pub modified: u64,
907    /// Flags set to undefined values by the instruction (as a bitmask)
908    pub undefined: u64,
909}
910
911/// CPU flag bit positions (matching Zydis definitions)
912#[derive(Debug, Clone, Copy, PartialEq, Eq)]
913#[repr(u8)]
914pub enum CPUFlag {
915    /// Carry Flag (bit 0)
916    CF = 0,
917    /// Parity Flag (bit 2)
918    PF = 2,
919    /// Auxiliary Carry Flag (bit 4)
920    AF = 4,
921    /// Zero Flag (bit 6)
922    ZF = 6,
923    /// Sign Flag (bit 7)
924    SF = 7,
925    /// Trap Flag (bit 8)
926    TF = 8,
927    /// Interrupt Enable Flag (bit 9)
928    IF = 9,
929    /// Direction Flag (bit 10)
930    DF = 10,
931    /// Overflow Flag (bit 11)
932    OF = 11,
933    /// I/O Privilege Level bit 0 (bit 12)
934    IOPL0 = 12,
935    /// I/O Privilege Level bit 1 (bit 13)
936    IOPL1 = 13,
937    /// Nested Task Flag (bit 14)
938    NT = 14,
939    /// Resume Flag (bit 16)
940    RF = 16,
941    /// Virtual 8086 Mode Flag (bit 17)
942    VM = 17,
943    /// Alignment Check Flag (bit 18)
944    AC = 18,
945    /// Virtual Interrupt Flag (bit 19)
946    VIF = 19,
947    /// Virtual Interrupt Pending Flag (bit 20)
948    VIP = 20,
949    /// CPUID Flag (bit 21)
950    ID = 21,
951}
952
953impl CpuFlags {
954    // Flag bit masks (using u64 for more flags)
955    /// Carry Flag mask
956    pub const CF: u64 = 1 << 0;
957    /// Parity Flag mask
958    pub const PF: u64 = 1 << 2;
959    /// Auxiliary Carry Flag mask
960    pub const AF: u64 = 1 << 4;
961    /// Zero Flag mask
962    pub const ZF: u64 = 1 << 6;
963    /// Sign Flag mask
964    pub const SF: u64 = 1 << 7;
965    /// Trap Flag mask
966    pub const TF: u64 = 1 << 8;
967    /// Interrupt Enable Flag mask
968    pub const IF: u64 = 1 << 9;
969    /// Direction Flag mask
970    pub const DF: u64 = 1 << 10;
971    /// Overflow Flag mask
972    pub const OF: u64 = 1 << 11;
973    /// I/O Privilege Level mask (bits 12-13)
974    pub const IOPL: u64 = (1 << 12) | (1 << 13);
975    /// Nested Task Flag mask
976    pub const NT: u64 = 1 << 14;
977    /// Resume Flag mask
978    pub const RF: u64 = 1 << 16;
979    /// Virtual 8086 Mode Flag mask
980    pub const VM: u64 = 1 << 17;
981    /// Alignment Check Flag mask
982    pub const AC: u64 = 1 << 18;
983    /// Virtual Interrupt Flag mask
984    pub const VIF: u64 = 1 << 19;
985    /// Virtual Interrupt Pending Flag mask
986    pub const VIP: u64 = 1 << 20;
987    /// CPUID Flag mask
988    pub const ID: u64 = 1 << 21;
989
990    /// Create new CPU flags info
991    #[must_use]
992    pub const fn new(read: u64, written: u64, modified: u64, undefined: u64) -> Self {
993        Self {
994            read,
995            written,
996            modified,
997            undefined,
998        }
999    }
1000
1001    /// Create CPU flags with only read and written (modified computed automatically)
1002    #[must_use]
1003    pub const fn from_read_written(read: u64, written: u64) -> Self {
1004        Self {
1005            read,
1006            written,
1007            modified: read & written, // Flags that are both read and written
1008            undefined: 0,
1009        }
1010    }
1011
1012    /// Check if any flag is read
1013    #[must_use]
1014    pub const fn reads_any(&self) -> bool {
1015        self.read != 0
1016    }
1017
1018    /// Check if any flag is written
1019    #[must_use]
1020    pub const fn writes_any(&self) -> bool {
1021        self.written != 0
1022    }
1023
1024    /// Check if any flag is modified
1025    #[must_use]
1026    pub const fn modifies_any(&self) -> bool {
1027        self.modified != 0
1028    }
1029
1030    /// Check if any flag is undefined
1031    #[must_use]
1032    pub const fn has_undefined(&self) -> bool {
1033        self.undefined != 0
1034    }
1035
1036    // Individual flag read checks
1037
1038    /// Check if the carry flag is read
1039    #[must_use]
1040    pub const fn reads_cf(&self) -> bool {
1041        (self.read & Self::CF) != 0
1042    }
1043
1044    /// Check if the zero flag is read
1045    #[must_use]
1046    pub const fn reads_zf(&self) -> bool {
1047        (self.read & Self::ZF) != 0
1048    }
1049
1050    /// Check if the sign flag is read
1051    #[must_use]
1052    pub const fn reads_sf(&self) -> bool {
1053        (self.read & Self::SF) != 0
1054    }
1055
1056    /// Check if the overflow flag is read
1057    #[must_use]
1058    pub const fn reads_of(&self) -> bool {
1059        (self.read & Self::OF) != 0
1060    }
1061
1062    /// Check if the parity flag is read
1063    #[must_use]
1064    pub const fn reads_pf(&self) -> bool {
1065        (self.read & Self::PF) != 0
1066    }
1067
1068    /// Check if the auxiliary carry flag is read
1069    #[must_use]
1070    pub const fn reads_af(&self) -> bool {
1071        (self.read & Self::AF) != 0
1072    }
1073
1074    /// Check if the direction flag is read
1075    #[must_use]
1076    pub const fn reads_df(&self) -> bool {
1077        (self.read & Self::DF) != 0
1078    }
1079
1080    // Individual flag write checks
1081
1082    /// Check if the carry flag is written
1083    #[must_use]
1084    pub const fn writes_cf(&self) -> bool {
1085        (self.written & Self::CF) != 0
1086    }
1087
1088    /// Check if the zero flag is written
1089    #[must_use]
1090    pub const fn writes_zf(&self) -> bool {
1091        (self.written & Self::ZF) != 0
1092    }
1093
1094    /// Check if the sign flag is written
1095    #[must_use]
1096    pub const fn writes_sf(&self) -> bool {
1097        (self.written & Self::SF) != 0
1098    }
1099
1100    /// Check if the overflow flag is written
1101    #[must_use]
1102    pub const fn writes_of(&self) -> bool {
1103        (self.written & Self::OF) != 0
1104    }
1105
1106    /// Check if the parity flag is written
1107    #[must_use]
1108    pub const fn writes_pf(&self) -> bool {
1109        (self.written & Self::PF) != 0
1110    }
1111
1112    /// Check if the auxiliary carry flag is written
1113    #[must_use]
1114    pub const fn writes_af(&self) -> bool {
1115        (self.written & Self::AF) != 0
1116    }
1117
1118    /// Check if the direction flag is written
1119    #[must_use]
1120    pub const fn writes_df(&self) -> bool {
1121        (self.written & Self::DF) != 0
1122    }
1123
1124    // Individual flag modify checks
1125
1126    /// Check if the carry flag is modified
1127    #[must_use]
1128    pub const fn modifies_cf(&self) -> bool {
1129        (self.modified & Self::CF) != 0
1130    }
1131
1132    /// Check if the zero flag is modified
1133    #[must_use]
1134    pub const fn modifies_zf(&self) -> bool {
1135        (self.modified & Self::ZF) != 0
1136    }
1137
1138    /// Check if the sign flag is modified
1139    #[must_use]
1140    pub const fn modifies_sf(&self) -> bool {
1141        (self.modified & Self::SF) != 0
1142    }
1143
1144    /// Check if the overflow flag is modified
1145    #[must_use]
1146    pub const fn modifies_of(&self) -> bool {
1147        (self.modified & Self::OF) != 0
1148    }
1149
1150    // Check if a specific flag is undefined
1151    /// Check if the carry flag is undefined after execution
1152    #[must_use]
1153    pub const fn undefined_cf(&self) -> bool {
1154        (self.undefined & Self::CF) != 0
1155    }
1156
1157    /// Check if the zero flag is undefined after execution
1158    #[must_use]
1159    pub const fn undefined_zf(&self) -> bool {
1160        (self.undefined & Self::ZF) != 0
1161    }
1162
1163    /// Check if the sign flag is undefined after execution
1164    #[must_use]
1165    pub const fn undefined_sf(&self) -> bool {
1166        (self.undefined & Self::SF) != 0
1167    }
1168
1169    /// Check if the overflow flag is undefined after execution
1170    #[must_use]
1171    pub const fn undefined_of(&self) -> bool {
1172        (self.undefined & Self::OF) != 0
1173    }
1174
1175    /// Check if a specific flag is read
1176    #[must_use]
1177    pub const fn reads_flag(&self, flag: CPUFlag) -> bool {
1178        (self.read & (1 << flag as u8)) != 0
1179    }
1180
1181    /// Check if a specific flag is written
1182    #[must_use]
1183    pub const fn writes_flag(&self, flag: CPUFlag) -> bool {
1184        (self.written & (1 << flag as u8)) != 0
1185    }
1186
1187    /// Check if a specific flag is modified
1188    #[must_use]
1189    pub const fn modifies_flag(&self, flag: CPUFlag) -> bool {
1190        (self.modified & (1 << flag as u8)) != 0
1191    }
1192
1193    /// Check if a specific flag is undefined
1194    #[must_use]
1195    pub const fn is_undefined(&self, flag: CPUFlag) -> bool {
1196        (self.undefined & (1 << flag as u8)) != 0
1197    }
1198
1199    /// Get flags that are either read or written
1200    #[must_use]
1201    pub const fn accessed(&self) -> u64 {
1202        self.read | self.written
1203    }
1204
1205    /// Merge with another CpuFlags
1206    #[must_use]
1207    pub const fn merge(&self, other: &Self) -> Self {
1208        Self {
1209            read: self.read | other.read,
1210            written: self.written | other.written,
1211            modified: self.modified | other.modified,
1212            undefined: self.undefined | other.undefined,
1213        }
1214    }
1215}
1216
1217#[cfg(test)]
1218mod tests {
1219    use super::*;
1220
1221    #[test]
1222    fn test_decoded_instruction_new() {
1223        let instr = DecodedInstruction::new();
1224        assert_eq!(instr.mnemonic, Mnemonic::Invalid);
1225        assert_eq!(instr.length, 0);
1226        assert!(!instr.is_valid());
1227    }
1228
1229    #[test]
1230    fn test_instruction_operands() {
1231        let mut instr = DecodedInstruction::new();
1232        instr.operand_count = 2;
1233
1234        let count = instr.operands().count();
1235        assert_eq!(count, 2);
1236    }
1237
1238    #[test]
1239    fn test_branch_target() {
1240        let mut instr = DecodedInstruction::new();
1241        instr.mnemonic = Mnemonic::JMP;
1242        instr.attributes = InstructionAttributes::IS_BRANCH | InstructionAttributes::IS_RELATIVE;
1243        instr.length = 5;
1244        instr.address = 0x1000;
1245        instr.displacement = 0x10;
1246
1247        let target = instr.branch_target();
1248        assert_eq!(target, Some(0x1015)); // 0x1000 + 5 + 0x10
1249    }
1250
1251    #[test]
1252    fn test_cpu_flags() {
1253        let flags = CpuFlags::new(CpuFlags::CF | CpuFlags::ZF, CpuFlags::OF, 0, 0);
1254        assert!(flags.reads_cf());
1255        assert!(flags.reads_zf());
1256        assert!(!flags.reads_sf());
1257        assert!(flags.writes_cf() == false);
1258        assert!(flags.writes_of());
1259    }
1260
1261    #[test]
1262    fn test_cpu_flags_from_read_written() {
1263        let flags =
1264            CpuFlags::from_read_written(CpuFlags::CF | CpuFlags::ZF, CpuFlags::CF | CpuFlags::OF);
1265        assert!(flags.reads_cf());
1266        assert!(flags.reads_zf());
1267        assert!(flags.writes_cf());
1268        assert!(flags.writes_of());
1269        assert!(flags.modifies_cf()); // CF is both read and written
1270        assert!(!flags.modifies_zf()); // ZF is only read
1271        assert!(!flags.modifies_of()); // OF is only written
1272    }
1273
1274    #[test]
1275    fn test_decoded_instruction_cpu_flags() {
1276        let mut instr = DecodedInstruction::new();
1277        instr.cpu_flags = CpuFlags::from_read_written(CpuFlags::CF, CpuFlags::ZF);
1278
1279        assert!(instr.reads_any_cpu_flag());
1280        assert!(instr.writes_any_cpu_flag());
1281        assert!(instr.reads_cpu_flag(CPUFlag::CF));
1282        assert!(instr.writes_cpu_flag(CPUFlag::ZF));
1283        assert!(!instr.reads_cpu_flag(CPUFlag::ZF));
1284    }
1285
1286    #[test]
1287    fn test_stack_pointer_info_push() {
1288        let info = StackPointerInfo::push(8);
1289        assert!(info.is_stack_modifying());
1290        assert!(info.is_stack_push());
1291        assert!(!info.is_stack_pop());
1292        assert_eq!(info.offset(), 8);
1293        assert_eq!(info.get_data_size(), 8);
1294    }
1295
1296    #[test]
1297    fn test_stack_pointer_info_pop() {
1298        let info = StackPointerInfo::pop(8);
1299        assert!(info.is_stack_modifying());
1300        assert!(!info.is_stack_push());
1301        assert!(info.is_stack_pop());
1302        assert_eq!(info.offset(), -8);
1303        assert_eq!(info.get_data_size(), 8);
1304    }
1305
1306    #[test]
1307    fn test_stack_pointer_info_call() {
1308        let info = StackPointerInfo::call(8);
1309        assert!(info.is_stack_modifying());
1310        assert!(info.is_call_instruction());
1311        assert!(!info.is_ret_instruction());
1312        assert_eq!(info.offset(), 8);
1313    }
1314
1315    #[test]
1316    fn test_stack_pointer_info_ret() {
1317        let info = StackPointerInfo::ret(8);
1318        assert!(info.is_stack_modifying());
1319        assert!(!info.is_call_instruction());
1320        assert!(info.is_ret_instruction());
1321        assert_eq!(info.offset(), -8);
1322    }
1323
1324    #[test]
1325    fn test_stack_pointer_info_enter() {
1326        let info = StackPointerInfo::enter(32, 0, 8);
1327        assert!(info.is_stack_modifying());
1328        // ENTER pushes RBP (8) and subtracts frame_size (32)
1329        assert_eq!(info.offset(), 8 + 32);
1330    }
1331
1332    #[test]
1333    fn test_stack_pointer_info_leave() {
1334        let info = StackPointerInfo::leave(8);
1335        assert!(info.is_stack_modifying());
1336        assert!(info.is_stack_pop());
1337        assert_eq!(info.offset(), -8);
1338    }
1339
1340    #[test]
1341    fn test_decoded_instruction_stack_pointer() {
1342        let mut instr = DecodedInstruction::new();
1343        instr.stack_pointer_info = StackPointerInfo::push(8);
1344
1345        assert!(instr.modifies_stack_pointer());
1346        assert!(instr.is_push());
1347        assert!(!instr.is_pop());
1348        assert_eq!(instr.stack_pointer_offset(), 8);
1349    }
1350}