//! Processor state stored in the EFLAGS register.

use bitflags::*;

use crate::Ring;

bitflags! {
    /// The EFLAGS register.
    pub struct EFlags: u32 {
        /// ID Flag (ID)
        const FLAGS_ID = 1 << 21;
        /// Virtual Interrupt Pending (VIP)
        const FLAGS_VIP = 1 << 20;
        /// Virtual Interrupt Flag (VIF)
        const FLAGS_VIF = 1 << 19;
        /// Alignment Check (AC)
        const FLAGS_AC = 1 << 18;
        /// Virtual-8086 Mode (VM)
        const FLAGS_VM = 1 << 17;
        /// Resume Flag (RF)
        const FLAGS_RF = 1 << 16;
        /// Nested Task (NT)
        const FLAGS_NT = 1 << 14;
        /// I/O Privilege Level (IOPL) 0
        const FLAGS_IOPL0 = 0b00 << 12;
        /// I/O Privilege Level (IOPL) 1
        const FLAGS_IOPL1 = 0b01 << 12;
        /// I/O Privilege Level (IOPL) 2
        const FLAGS_IOPL2 = 0b10 << 12;
        /// I/O Privilege Level (IOPL) 3
        const FLAGS_IOPL3 = 0b11 << 12;
        /// Overflow Flag (OF)
        const FLAGS_OF = 1 << 11;
        /// Direction Flag (DF)
        const FLAGS_DF = 1 << 10;
        /// Interrupt Enable Flag (IF)
        const FLAGS_IF = 1 << 9;
        /// Trap Flag (TF)
        const FLAGS_TF = 1 << 8;
        /// Sign Flag (SF)
        const FLAGS_SF = 1 << 7;
        /// Zero Flag (ZF)
        const FLAGS_ZF = 1 << 6;
        /// Auxiliary Carry Flag (AF)
        const FLAGS_AF = 1 << 4;
        /// Parity Flag (PF)
        const FLAGS_PF = 1 << 2;
        /// Bit 1 is always 1.
        const FLAGS_A1 = 1 << 1;
        /// Carry Flag (CF)
        const FLAGS_CF = 1 << 0;
    }
}

impl EFlags {
    /// Creates a new Flags entry. Ensures bit 1 is set.
    pub const fn new() -> EFlags {
        EFlags::FLAGS_A1
    }

    /// Creates a new Flags with the given I/O privilege level.
    pub const fn from_priv(iopl: Ring) -> EFlags {
        EFlags {
            bits: (iopl as u32) << 12,
        }
    }
}

#[cfg(target_arch = "x86")]
#[inline(always)]
pub unsafe fn read() -> EFlags {
    let r: u32;
    llvm_asm!("pushfl; popl $0" : "=r"(r) :: "memory");
    EFlags::from_bits_truncate(r)
}

#[cfg(target_arch = "x86")]
#[inline(always)]
pub unsafe fn set(val: EFlags) {
    llvm_asm!("pushl $0; popfl" :: "r"(val.bits()) : "memory" "flags");
}

/// Clears the AC flag bit in EFLAGS register.
///
/// This disables any alignment checking of user-mode data accesses.
/// If the SMAP bit is set in the CR4 register, this disallows
/// explicit supervisor-mode data accesses to user-mode pages.
///
/// # Unsafe
///
/// This instruction is only valid in Ring 0 and requires
/// that the CPU supports the instruction (check CPUID).
#[inline(always)]
pub unsafe fn clac() {
    llvm_asm!("clac" ::: "memory" "flags" : "volatile");
}

/// Sets the AC flag bit in EFLAGS register.
///
/// This may enable alignment checking of user-mode data accesses.
/// This allows explicit supervisor-mode data accesses to user-mode
/// pages even if the SMAP bit is set in the CR4 register.
///
/// # Unsafe
///
/// This instruction is only valid in Ring 0 and requires
/// that the CPU supports the instruction (check CPUID).
#[inline(always)]
pub unsafe fn stac() {
    llvm_asm!("stac" ::: "memory" "flags" : "volatile");
}
